RabbitMQ—流控机制

 

一、为什么进行流控

当RabbitMQ发布消息速度快于消费速度或者系统资源不足时,RabbitMQ为避免服务器资源饱满而宕机将降低或阻断发布消息速度。可以通过rabbitmqctl和web管理页面查看连接的状态为flow,当服务器资源不足时,连接会被blocked。连接被流控后和正常使用没有什么区别。

二、流控手段

1.内存控制

RabbitMQ服务器在启动时会计算系统内存总大小。然后会根据vm_memory_high_watermark参数指定的百分比,进行控制.可通过命令 rabbitmqctl set_vm_memory_high_watermark fraction 动态设置。默认下,vm_memory_high_watermark的值为0.4,当RabbitMQ使用的内存超过40%时,系统会阻塞所有连接。一旦警报解除(消息被消费者取走,或者消息被写到硬盘中),系统重新恢复工作。32位系统的单进程内存使用最大为2G,即使系统有10G,那么内存警报线为2G*40%。

在日志中可以找到内存限制的相关信息

例如 :/var/log/rabbitmq/
=INFO REPORT==== 28-Apr-2015::14:11:16 ===
Memory limit set to 3804MB of 7609MB total.12345

当vm_memory_high_watermark为0时,不再有内存警报。并且所有的消息发布都将停止,这个方法可用来禁止消息发布的情况。

2.磁盘控制

当RabbitMQ的磁盘空闲空间小于50M(默认),生产者将被BLOCK,并且阻塞信息发布前,会尝试把内存中的信息输出到磁盘上(持久化,避免马上宕机引起数据丢失)。持久化信息和非持久化信息都将被写到磁盘(持 久化信息一进入队列就会被写到磁盘)。如果采用集群模式,磁盘节点空闲空间小于50M将导致其他节点的生产者都被block。可以通过disk_free_limit来对进行配置。 如果磁盘的预设值为50%,内存预设值默认是0.4,那么就是当内存使用量达到20%时,队列信息会被写到磁盘上。 可以通过设置 vm_memory_high_watermark_paging_ratio (默认值为0.5)来改变写入磁盘的策略,例如:

[{rabbit, [{vm_memory_high_watermark_paging_ratio, 0.75},          {vm_memory_high_watermark, 0.4}]}].1

上面的配置将会在内存全用30%时将队列信息写入到磁盘,阻塞信息发布是在内存使用40%时发生。建议vm_memory_high_watermark 不超过50%.

3.消息积压

在RabbitMQ中,消息可能被存储在多个不同的队列,消息越早被消费,那么消息经过的队列层次越少,则平均每个消息处理的开销就越小。但若发布消息的速率过快,MQ来不及处理,这些消息就可能进入很深层次的队列,大大增加平均每个消息的处理开销,进一步使得处理新消息和发送旧消息的能力减弱,更多的消息会进入很深的队列,循环往复,整个系统的性能就会极大的降低。另外若接收消息的速率过快还会实现某些进程的mailbox过大,可能会产生很严重的后果。为此,RabbitMQ设计了一套流控机制。 
RabbitMQ 使用了一种基于 credit 的算法来 限制 message 被 publish 的速率 。Publisher 只有在其从某个 queue 的 全部镜像处收到 credit 之后才被允许继续 publish 。在这个上下文中,Credit 意味着对 publish 行为的允许。如果存在没能成功发送出 credit 的 slaves ,则将导致 publisher 停止 publish 动作。Publisher 会一直保持停止的状态,直到所有 slave 都成功发送了 credit 或者直到剩余的 node 都认为某 slave 已经从 cluster 中断开了。Erlang 会周期性地发送 tick 到所有的 node 上来检测是否出现连接断开。 tick 的间隔时间可以通过配置 net_ticktime 的值来控制。

三、流控机制原理

1.如何开关闸门

RabbitMQ使用TCP长连接进行通讯,接收数据的起点进程为rabbit_reader。首先分析它的接收loop

recvloop(Deb, State = #v1{connection_state = blocked}) ->  
mainloop(Deb, State);
recvloop(Deb, State = #v1{sock = Sock, recv_len = RecvLen, buf_len = BufLen})
  when BufLen<RecvLen ->
      ok = rabbit_net:setopts(Sock, [{active, once}]),
mainloop(Deb, State#v1{pending_recv = true});  
recvloop(Deb, State = #v1{recv_len = RecvLen, buf = Buf, buf_len = BufLen}) -> 
    {Data, Rest} = split_binary(case Buf of  
        [B] -> B;  _ ->list_to_binary(lists:reverse(Buf)) end, RecvLen), 
recvloop(Deb, handle_input(State#v1.callback, Data, State#v1{buf = [Rest],  
buf_len = BufLen - RecvLen})).

从上面代码可以看出,rabbit_reader每接收到一个包,就设置套接字属性为{active, once},若当前连接被blocked时则不设置{active,once},这个接收进程就阻塞在receive方法上。通过这种方式来实现闸门的开关。

https://wenku.baidu.com/view/f18969d684254b35eefd348b.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值