MNS是阿里云提供的开箱即用的 队列服务,容易上手。当数据量很大,特别是做大批量数据同步时,很多人就会遇到性能瓶颈。
下面就给大家介绍下,提升单机MNS client性能的几种方法。
1. 网络环境
首先考虑的是网络环境。
使用内网endpoint吧,不细说。
2. 多线程的正确姿势
其次,利用多线程提升并发度。
这个大家肯能都会想到,不过事情并没有那么简单。看看官网的多线程示例,我是例子。
嗯。。。100个线程,tps怎么也得10+倍了吧。但是没卵用,多线程似乎失效?
so翻了翻源码,当看到MNS底层使用的http reactor模块,DefaultConnectingIOReactor、AbstractMultiworkerIOReactor时,问题逐渐变得明朗起来。
class AbstractMultiworkerIOReactor {
private final int workerCount;
private final ThreadFactory threadFactory;
private final BaseIOReactor[] dispatchers;
private final Worker[] workers;
private final Thread[] threads;
原来事情的真相是这样:
MNS client 是个singleton。官网的例子用这个singleton搞出了一大堆CloudQueue,想要发挥多线程的魔力。
其实看图可知,不论搞出多少CloudQueue,下面共用的是io 线程池和连接池,而它们才是真正干活的啊。
这种感觉就好比,一家饭店,找了大堆服务员在门口拉客,但后面就一个厨子。
所以,单纯增加CloudQueue没有用,相反,由于CloudQueue放任务到IO池里都是内存操作是很快的,并不要很多线程。
道理明白后,要做的事情就简单了,多线程的正确姿势是:增多worker。
ClientConfiguration clientConf = new ClientConfiguration();
clientConf.setMaxConnections(很多连接);
clientConf.setIoReactorThreadCount(很多线程);
CloudAccount account = new CloudAccount(
mnsConfig.getAccessId(),
mnsConfig.getAccessKey(),
mnsConfig.getEndPoint(),
clientConf);
3. Batch的Magic Number
再次,批量处理。像Kafka、flume等都是加大batch提升吞吐量的。
MNS client SDK也有类似的API,如
List<Message> batchPopMessage(int batchSize);
void batchDeleteMessage(List<String> receiptHandles);
batchPutMessage(List<Message> messages)
激动地给batch设了个100,Run,服务端异常,50,异常,20异常。说好的batch呢,难道是个假API?
当试到16这个Magic Number时,终于成功了。翻看下官网文档,也没有对magic number说明。
4. 测试小结
简单测试了下,4core8G,单条消息1k byte。
4线程: total send message 16000, cost 5681, tps 2816
8线程: total send message 16000, cost 4603, tps 3475
16线程:total send message 16000, cost 1973, tps 8109
敬告:对于那些一不小心就超过上面测试数据的同学,欢迎加入橙鹰数据(据说是杭州一个很牛x很低调的大数据公司)。
BTW, 优化的意义。下面是阿里云另一款高性能MQ产品,
5. 坑货
5.1巨坑之PollingWaitSeconds
控制台创建MNS队列时,PollingWaitSeconds默认值是0。
这个值必须要设置
这个值必须要设置
这个值必须要设置
因为MNS会按请求次数收费。依稀记得那个晚上,几w块就灰飞烟灭了。推荐设10~30。
5.2 小坑之VisibilityTimeout
使用过Kafka,Metaq的同学知道,消息消费掉后,会commit offset告诉broker。
不同的地方是,在使用MNS时,为了不丢数据,通常是消费成功后,手动删除消息。
如果消息被消费后,超过了VisibilityTimeout,再去删除这个消息,就会遇到喜闻乐见的Not Found错误。
5.3 小坑之重建队列
常会有这种情景,给队列发送了很多消息,想把消息全部删除后重新灌批数据,但是由于没有一键清除的方法,用户会删除该队列,然后重建个同名的队列。
这样产生的问题是,原来的MNS client找不到该队列了,必须要重启。