rabbitmq为了能够保证服务器在大量使用资源的情况下正常工作,会做流控。

所谓流控有以下两个方面。一是针对连接做流控,即降低某频率过快的发送消息。二是整体流控,即将所有消费者发送的消息丢掉,悄无声息。

首先是针对连接做的流控,per-connection

rabbitmq通过使用credit_flow来实现连接级别的流控.假设有这样的数据流向,A->B->C,如果C消息处理不及时,B能够感得到,则B会减少A发送的消息,从而从源头作到流控.rabbitmq消息有如下的流向,rabbit_reader->rabbit_channel->rabbit_amqqueue->rabbit_msg_store,如果中间某个模块处理消息不及时,会导致最终从源头rabbit_reader完成阻塞工作,进而实现流控.下面来看下这个功能是如果实现的.

先介绍credit,信用.A向B发消息,第次发消息都会使A中B的credit减1,如果降到0的话A就会block; B收到消息后会向A作ack,每次ack都会使的A中B的credit增加.

-define(DEFAULT_CREDIT, {200, 50}).

A中B的credit初始值,200, 每次B向A作ack都会使得A中B的credit加50.

以rabbit_reader与rabbit_channel为例. rabbit_reader向rabbit_channel发消息时会做rabbit_channel:do_flow/3操作,

 process_frame(Frame, Channel, State) ->
ChKey = {channel, Channel},
case (case get(ChKey) of
undefined -> create_channel(Channel, State);
Other -> {ok, Other, State}
end) of
{ok, {ChPid, AState}, State1} ->
case rabbit_command_assembler:process(Frame, AState) of
{ok, Method, Content, NewAState} ->
rabbit_channel:do_flow(ChPid, Method, Content),
put(ChKey, {ChPid, NewAState}),
post_process_frame(Frame, ChPid, control_throttle(State1));
end
end.

rabbit_channel:do_flow会让rabbit_reader将rabbit_channel的credit减1,并通过让rabbitmq_channel处理method并对此次消息发送作credit_flow:ack,即

do_flow(Pid, Method, Content) ->
credit_flow:send(Pid),
gen_server2:cast(Pid, {method, Method, Content, flow}).
credit_flow:send/1会在rabbit_reader中以进程字典的形式记录rabbit_channel的credit. rabbit_reader中rabbit_channel的credit以1为单位减少,如果为0的话即作block.如果credit小于0,rabbit_reader仍会给rabbit_channel发消息.
 send(From) -> send(From, ?DEFAULT_CREDIT).

 send(From, {InitialCredit, _MoreCreditAfter}) ->
?UPDATE({credit_from, From}, InitialCredit, C,
if C == 1 -> block(From),
0;
true -> C - 1
end).

如果credit为0的话,rabbit_reader会将rabbit_channel置入credit_blocked进程字典中,

 block(From) ->
case blocked() of
false -> put(credit_blocked_at, erlang:now());
true -> ok
end,
?UPDATE(credit_blocked, [], Blocks, [From | Blocks]).

正常情况下,在收到消息之后会给ack,当积累足够的时候才会向rabbit_reader申请更多的credit.

 ack(To) -> ack(To, ?DEFAULT_CREDIT).

 ack(To, {_InitialCredit, MoreCreditAfter}) ->
?UPDATE({credit_to, To}, MoreCreditAfter, C,
if C == 1 -> grant(To, MoreCreditAfter),
MoreCreditAfter;
true -> C - 1
end).

在向rabbit_reader申请更多的credit的时候会发rabbit_reader发送消息{bump_credit, {self(), Quantity}},如果自己当前的状态是blocked的话,就不会申请更多的credit,因为现在不需要接收消息了.

 grant(To, Quantity) ->
Msg = {bump_credit, {self(), Quantity}},
case blocked() of
false -> To ! Msg;
true -> ?UPDATE(credit_deferred, [], Deferred, [{To, Msg} | Deferred])
end.

rabbit_reader收到消息之后会将增加{credit_from, From}的值,control_throttle是用来改变当前连接的状态,running,blocking,blocked

 handle_other({bump_credit, Msg}, State) ->
credit_flow:handle_bump_msg(Msg),
control_throttle(State);

如果之前是blocked状态,在增加了{credit_from, From}的值之后,需要可能需要unblock操作,unblock即是将之前申请block时未成功申请的credit,再依次做申请,欠的迟早要还.

 unblock(From) ->
?UPDATE(credit_blocked, [], Blocks, Blocks -- [From]),
case blocked() of
false -> case erase(credit_deferred) of
undefined -> ok;
Credits -> [To ! Msg || {To, Msg} <- Credits]
end;
true -> ok
end.

"最终进程阻塞在mainloop/2的rabbit_net:recv/1函数上。rabbit_net:recv/1函数会阻塞的原因是RabbitMQ采用了gen_tcp的半阻塞模型,也就是说每次接受一个tcp消息之后,必须显式调用inet:setopts(Sock, [{active, once}])来激活一下,否则,进程会一直阻塞在receive语句上。"

总结:

消息发送者拥有{credit_from, From}用来知道给哪些消息接收者发送了消息,以便在任何一个接收者出现credit不足的时候,消息发送者都将会把自己置入一个blocked的状态

接收者拥有{credit_to, To}根据此项来知道是否需要向消息发送者申请更多的credit.

一个进程即可以作为消息发送者,也可以作为消息接收者,那么在自己处于blocked的状态下,{credit_to, To}时不会再申请更多的credit,这样会导致发送者进入blocked状态,最终使得整个工作流blocked,没有消息流通,进而达到流控的效果.

参考文献:

衔山. RabbitMQ源码分析:Per-Connection流控机制. http://fengchj.com/?p=2084

rabbimq之流控的更多相关文章

  1. [RouterOS] ROS对接碧海威或PA等流控实现完美流控详细教程(附脚本全免费)

    前言: 经常在群里看到不少朋友争论海蜘蛛 ROS 维盟 爱快 碧海威 流控大师 Woyos等等软路由,哪个好.实际上,网络产品是复杂的,现在的软路由功能上已经远远不是单独的路由了.每种产品都有他本身的 ...

  2. 【转】android中最好的瀑布流控件PinterestLikeAdapterView

    [源地址]http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0919/1696.html 之前我们介绍过一个开源的瀑布流控件Stag ...

  3. [tty与uart]UART中的硬件流控RTS与CTS

    转自:http://blog.csdn.net/zeroboundary/article/details/8966586 在RS232中本来CTS 与RTS 有明确的意义,但自从贺氏(HAYES ) ...

  4. Android 高级UI设计笔记10:瀑布流控件PinterestLikeAdapterView的使用

    1. 首先我们看看瀑布流的效果,如下: 2. 今天要介绍的瀑布流控件是:PinterestLikeAdapterView 项目地址:https://github.com/GDG-Korea/Pinte ...

  5. ActiveMQ内存设置和流控

    启动脚本设置jvm的内存 if "%ACTIVEMQ_OPTS%" == "" set ACTIVEMQ_OPTS=-Xms1G-Xmx1G -Djava.ut ...

  6. Linux下使用虚拟网卡的ingress流控(入口流控)

    Linux内核实现了数据包的队列机制,配合多种不同的排队策略,可以实现完美的流量控制和流量整形(以下统称流控).流控可以在两个地方实现,分别为egress和ingress,egress是在数据包发出前 ...

  7. 性能百万/s:腾讯轻量级全局流控方案详解

    WeTest 导读 全新的全局流控实现方案,既解决了目前流控的实现难点,同时保证运行稳定且流控准确的前提下,实现更简单,部署成本更低,容灾能力更强. 该方案组件化之后,可以推广到别的有需要的部门使用, ...

  8. 统一流控服务开源-1:场景&业界做法&算法篇

    最近团队在搞流量安全控制,为了应对不断增大的流量安全风险.Waf防护能做一下接入端的拦截,但是实际流量会打到整个分布式系统的每一环:Nginx.API网关.RPC服务.MQ消息应用中心.数据库.瞬间的 ...

  9. 又拍云张聪:OpenResty 动态流控的几种姿势

    2019 年 1 月 12 日,由又拍云.OpenResty 中国社区主办的 OpenResty × Open Talk 全国巡回沙龙·深圳站圆满结束,又拍云首席架构师张聪在活动上做了< Ope ...

随机推荐

  1. AdaBoost 和 Real Adaboost 总结

    AdaBoost 和 Real Adaboost 总结 AdaBoost Real AdaBoost AdaBoost AdaBoost, Adaptive Boosting(自适应增强), 是一种集 ...

  2. hiveserver2

    hiveserver2 默认绑定了ip:localhost 和 port:10000 !connect jdbc:hive2://localhost:10000  org.apache.hive.jd ...

  3. java线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  4. 在Web应用中接入微信支付的流程之极简清晰版 (转)

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

  5. ACM: FZU 2148 Moon Game - 海伦公式

     FZU 2148  Moon Game Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64 ...

  6. HDU 4753 Fishhead’s Little Game(DFS)

    题目链接 很繁琐的爆搜,最多要加2^12条边,暴力就可以,回溯那部分一直没有回溯好,写了一晚上...代码非常,非常难看..对了,还不是普通的爆搜,双向搜索博弈,以前记得看过,这次好像第一次写.. #i ...

  7. 【bzoj3527】[Zjoi2014]力 FFT

    2016-06-01  21:36:44 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3527 我就是一个大傻叉 微笑脸 #include&l ...

  8. 20145330《Java程序设计》第二次实验报告

    20145330<Java程序设计>第二次实验报告 实验二 Java面向对象程序设计 实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承多态 3.初步掌握UM ...

  9. Makefile简易模板

    MAKE = g++ -g #MAKE = gcc -g FILES = tf all : $(FILES) #DYSRC = target.c #DYTGT = $(DYSRC:.c=.o) %.o ...

  10. C#操作文件夹及文件的方法的使用

    本文收集了目前最为常用的C#经典操作文件的方法,具体内容如下:C#追加.拷贝.删除.移动文件.创建目录.递归删除文件夹及文件.指定文件夹下面的所有内容copy到目标文件夹下面.指定文件夹下面的所有内容 ...