写在前面
最开始我们使用rabbitmq
的时候可能只会用一个消费和一个prefetch Count
,如下图所示。
随着生产者发送消息量变大,等到生产者发送速度大于消费者消费速度就会产生消息挤压。这个时候我就要考虑设置设置并发数和
prefetch Count
。
设置prefetch Count
https://www.rabbitmq.com/blog/2014/04/14/finding-bottlenecks-with-rabbitmq-3-3/这篇博客告诉我们设置多少合理。经过作者的测试,preprefetch Count
设置30是合理的,如果preprefetch Count
值再变大,消费速度基本没变。这篇博客也告诉了我们一个观察消费速率的指标Consumer utilisation
,对应的这个值越大,消费投放速度越快,消息处于空闲的时间会越短。
并发数
试想以下场景,消费者设置一个并发,一个prefetch count
,这个时候rabbitmq
中有A
,B
两条消息,其中A
消息推送给了消费者,B
消息还在rabbitmq
队列中,消费者处理A
消息需要1s。这个时候消费者在处理A
消息,但是B
消息还在队列中休息,静静的等着推送给消费者,B
消息休息的时间就是消息的空闲时间。
下图中显示了
B
消息的空闲期。增大并发数也可以减少
B
消息的空闲时间,那么并发数设置多少合适??并发数设置大了就会加大线程对资源抢夺,增加上下文切换频率等反而有可能会降低消费速度,并发数少了CPU
的利用率低。这里提供一个公式:并发数=核数 * (x+y)/x
,其中x
是线程执行时间,y
是阻塞等待时间。关于阻塞:
- 这里的阻塞指的是会释放CPU的阻塞,比如网络编程的accept()等待客户端连接,recv()等待下游回包。
-
while(true) { i ++;}
这种阻塞就不会释放CPU
。
关于公式推导:
如下图,假设是单核CPU
,并且线程工作时间需要2ms,阻塞时间为2ms,那么只要设置两个线程就可以让CPU
跑到100%。
可以看到,只要在前一个线程一旦处于空闲时间中,后面的任务能够立马被执行,线程就可以跑到100%,如果单核
CPU
,线程工作2ms,阻塞时间为3ms,那么我们就需要3个线程保证cpu跑到100%,所以线程数就是(执行时间+空闲时间)/执行时间
,如果有小数,那么就向上取整。所以如果是N
核CPU
,最佳的设置的线程数是=N*((执行时间+空闲时间)/执行时间)
。所以在单机上,
rabbitmq
消费者设置的合理并发数是:N*((消费消费时间+消息在网络传递时间)/消费消费时间)