rabbitmq之消息重入队列
说起消息重入队列还得从队列注册消费者说起,客户端在向队列注册消费者之后,创建的channel也会被主队列进程monitor,当channel挂掉后,主队列进程(rabbit_amqqueue_process)收到'DOWN'通知,将未ack的消息重入队列,并根据消息的deliver tag,也就是消费入队列的顺序,将消息重入队列中
主要代码如下:
1.注册消费者
handle_method(#'basic.consume'{queue = QueueNameBin,
consumer_tag = ConsumerTag,
no_local = _, % FIXME: implement
no_ack = NoAck,
exclusive = ExclusiveConsume,
nowait = NoWait,
arguments = Args},
_, State = #ch{consumer_prefetch = ConsumerPrefetch,
consumer_mapping = ConsumerMapping}) ->
case dict:find(ConsumerTag, ConsumerMapping) of
error ->
QueueName = qbin_to_resource(QueueNameBin, State),
check_read_permitted(QueueName, State),
ActualConsumerTag =
case ConsumerTag of
<<>> -> rabbit_guid:binary(rabbit_guid:gen_secure(),
"amq.ctag");
Other -> Other
end,
case basic_consume(
QueueName, NoAck, ConsumerPrefetch, ActualConsumerTag,
ExclusiveConsume, Args, NoWait, State) of
{ok, State1} ->
{noreply, State1};
{error, exclusive_consume_unavailable} ->
rabbit_misc:protocol_error(
access_refused, "~s in exclusive use",
[rabbit_misc:rs(QueueName)])
end;
{ok, _} ->
%% Attempted reuse of consumer tag.
rabbit_misc:protocol_error(
not_allowed, "attempt to reuse consumer tag '~s'", [ConsumerTag])
end;
2.主队列进程增加消费者,并对channel进程监控
handle_call({basic_consume, NoAck, ChPid, LimiterPid, LimiterActive,
PrefetchCount, ConsumerTag, ExclusiveConsume, Args, OkMsg},
_From, State = #q{consumers = Consumers,
exclusive_consumer = Holder}) ->
case check_exclusive_access(Holder, ExclusiveConsume, State) of
in_use -> reply({error, exclusive_consume_unavailable}, State);
ok -> Consumers1 = rabbit_queue_consumers:add(
ChPid, ConsumerTag, NoAck,
LimiterPid, LimiterActive,
PrefetchCount, Args, is_empty(State),
Consumers),
end;
ch_record(ChPid, LimiterPid) ->
Key = {ch, ChPid},
case get(Key) of
undefined -> MonitorRef = erlang:monitor(process, ChPid),
Limiter = rabbit_limiter:client(LimiterPid),
C = #cr{ch_pid = ChPid,
monitor_ref = MonitorRef,
acktags = queue:new(),
consumer_count = 0,
blocked_consumers = priority_queue:new(),
limiter = Limiter,
unsent_message_count = 0},
put(Key, C),
C;
C = #cr{} -> C
end.
3.主队列进程收到channel 'DOWN'的消息后,删除消费者,获取此被此channel ack的消息
handle_info({'DOWN', _MonitorRef, process, DownPid, _Reason}, State) ->
case handle_ch_down(DownPid, State) of
{ok, State1} -> noreply(State1);
{stop, State1} -> stop(State1)
end;
handle_ch_down(DownPid, State = #q{consumers = Consumers,
exclusive_consumer = Holder,
senders = Senders}) ->
State1 = State#q{senders = case pmon:is_monitored(DownPid, Senders) of
false -> Senders;
true -> credit_flow:peer_down(DownPid),
pmon:demonitor(DownPid, Senders)
end},
case rabbit_queue_consumers:erase_ch(DownPid, Consumers) of
not_found ->
{ok, State1};
{ChAckTags, ChCTags, Consumers1} ->
case should_auto_delete(State2) of
true -> {stop, State2};
false -> {ok, requeue_and_run(ChAckTags,
ensure_expiry_timer(State2))}
end
end.
4.涉及重入队列时,需要了解backing queue,即消息是如何在镜像队列之间内部以及消息如何在本地内存和磁盘资源之间按需切换,或此部分涉及内容较多,后序会专门列出一个专题来分析此实现。rabbit_amqqueue_process主队列进程的backing_queue是rabbit_mirror_queue_master(镜像队列消息同步),后者因为需要将消息按需放置,所以也有backing_queueu,即rabbit_variable_queue。
根据sequeue_id来判断消息在队列中的位置,从当前队列中pop出队头的消息(最早入队列的消息),若未ack的消息较晚(seqid相对大),则将pop队头的队列再与未ack的消息比较,将消息pop出的消息放置在front队列中,直到符合,插入队列,并将front队列与此队列合入。
queue_merge(SeqIds, Q, MsgIds, Limit, PubFun, State) ->
queue_merge(SeqIds, Q, ?QUEUE:new(), MsgIds,
Limit, PubFun, State). queue_merge([SeqId | Rest] = SeqIds, Q, Front, MsgIds,
Limit, PubFun, State)
when Limit == undefined orelse SeqId < Limit ->
case ?QUEUE:out(Q) of
{{value, #msg_status { seq_id = SeqIdQ } = MsgStatus}, Q1}
when SeqIdQ < SeqId ->
%% enqueue from the remaining queue
queue_merge(SeqIds, Q1, ?QUEUE:in(MsgStatus, Front), MsgIds,
Limit, PubFun, State);
{_, _Q1} ->
%% enqueue from the remaining list of sequence ids
{MsgStatus, State1} = msg_from_pending_ack(SeqId, State),
{#msg_status { msg_id = MsgId } = MsgStatus1, State2} =
PubFun(MsgStatus, State1),
queue_merge(Rest, Q, ?QUEUE:in(MsgStatus1, Front), [MsgId | MsgIds],
Limit, PubFun, State2)
end;
queue_merge(SeqIds, Q, Front, MsgIds,
_Limit, _PubFun, State) ->
{SeqIds, ?QUEUE:join(Front, Q), MsgIds, State}.
rabbitmq之消息重入队列的更多相关文章
- RabbitMQ延迟消息:死信队列 | 延迟插件 | 二合一用法+踩坑手记+最佳使用心得
前言 前段时间写过一篇: # RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得 很多人加了我好友,说很喜欢这篇文章,也问了我一些问题. 因为最近工作比较忙, ...
- WinExec可能会引起消息重入
WinExec不仅会造成延迟,并且还会引起消息的重入. 以下是调用堆栈: WinvoiceCC.exe!CWinvoiceCCDlg::OnMsgHttpReq(unsigned int wParam ...
- rabbitmq设置消息优先级、队列优先级配置
1.首先在consume之前声明队列的时候,要加上x-max-priority属性,一般为0-255,大于255出错 -----配置队列优先级 配置成功后rabbitmq显示: 2.在向exchan ...
- RabbitMQ .NET消息队列使用入门(二)【多个队列间消息传输】
孤独将会是人生中遇见的最大困难. 实体类: DocumentType.cs public enum DocumentType { //日志 Journal = 1, //论文 Thesis = 2, ...
- RabbitMQ .NET消息队列使用入门(一)【简单示例】
首先下载安装包,我都环境是win7 64位: 去官网下载 otp_win64_19.0.exe 和rabbitmq-server-3.6.3.exe安装好 然后开始编程了: (1)创建生产者类: cl ...
- SpringBoot | 第三十八章:基于RabbitMQ实现消息延迟队列方案
前言 前段时间在编写通用的消息通知服务时,由于需要实现类似通知失败时,需要延后几分钟再次进行发送,进行多次尝试后,进入定时发送机制.此机制,在原先对接银联支付时,银联的异步通知也是类似的,在第一次通知 ...
- C#调用RabbitMQ实现消息队列
前言 我在刚接触使用中间件的时候,发现,中间件的使用并不是最难的,反而是中间件的下载,安装,配置才是最难的. 所以,这篇文章我们从头开始学习RabbitMq,真正的从头开始. 关于消息队列 其实消息队 ...
- 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁
1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...
- RabbitMQ分布式消息队列服务器(一、Windows下安装和部署)
RabbitMQ消息队列服务器在Windows下的安装和部署-> 一.Erlang语言环境的搭建 RabbitMQ开源消息队列服务是使用Erlang语言开发的,因此我们要使用他就必须先进行Erl ...
随机推荐
- java27
1:反射(理解) (1)类的加载及类加载器 (2)反射: 通过字节码文件对象,去使用成员变量,构造方法,成员方法 (3)反射的使用 A:通过反射获取构造方 ...
- Spring PropertyPlaceholderConfigurer数据库配置
pom.xml中添加依赖 <!-- mysql-connector-java --> <dependency> <groupId>mysql</groupId ...
- iOS UIStackView的理解
iOS9 提供的UIStackview简化了布局操作,它有些像Android中的liner layout.以前不用UIStackview也是可以做出类似的效果的,不过要添加许多的约束,看得头都大了,使 ...
- Android之界面(布局文件layput)
1.关于组件居中 ① android:layout_gravity="center" 控件在包含该控件的父控件中的位置.同样,当我们在Button按钮控件中设置android:l ...
- php接口
如果要继承多个类的方法规范,用接口,因为抽象类只能继承一个: 如果要共享一个方法体内容,用抽象类: <?php //接口是为了规范实现它的子类,以达到统一的目的 //接口不能被实例化 inter ...
- DTD文档模型和HTML基础
html是超文本标记语言,现在常用到的2中文档格式是html5和XHTML 1.0 Transitiona(过渡). <!DOCTYPE html> <!--当前文档为html5-- ...
- FTP协议及工作原理
1. FTP协议 什么是FTP呢?FTP 是 TCP/IP 协议组中的协议之一,是英文File Transfer Protocol的缩写. 该协议是Internet文件传送的基础,它由一系列规格说明文 ...
- error C2144: 语法错误:“int”的前面应有“;”
百度网上都说是中文输入的问题. 但我的错误是函数声明时后面忘了加 :真是要死......
- Objective-C 中self.与_访问方式的区别
Objective-C中属性self.a与_a访问的区别: 在OC中我们可以通过指令@property定义属性. OC对属性封装了许多方法,同时也会自动实现一些方法,相比实例变量,感觉更加面向对象些. ...
- CSS 百分比 margin & padding
前段时间我同事对于margin和padding应用百分比值似乎有些误解,觉得可能是个普遍问题,所以觉得有必要拿出来单独写一下. margin和padding都可以使用百分比值的,但有一点可能和通常的想 ...