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 ...
随机推荐
- secureCRT会话导入到xshell中的方法
官方给出了一个工具,sessionimporter.exe 不过软件有点老了,导入的会话路径和xshell默认的会话路径不一致,导致导入后,xshell没有导入的会话信息 sessionimporte ...
- C++代码重构——从C global到C++ template
在学数据结构的时候,我常有这样目标--写出能够最大程度复用的代码(算法正确,封装优秀).我常想--如何能在短时间内达成"算法正确,封装优秀"这样的目标.经过一段时间的摸索,我的结论 ...
- firefox
ctrl l 定位到地址栏 f6 同ctrl l ctrl k 定位到搜索框 shift delete 删除地址 ...
- java 项目中几种O实体类的概念
经常会接触到vo,do,dto的概念,本文从领域建模中的实体划分和项目中的实际应用情况两个角度,对这几个概念进行简析. 得出的主要结论是:在项目应用中,vo对应于页面上需要显示的数据(表单),do对应 ...
- Nginx与Apache的比较
Nginx与Apache的比较 Nginx相对于Apache的优点 轻量级.同样起web服务,比apache占用更少的资源和内存 抗并发.nginx处理请求是异步非阻塞,而apache则是阻塞型.在高 ...
- Linux之tomcat日志管理
tomcat 的日志输出catalina.out,变大,可使用下面方式解决. cronolog. http://blog.csdn.net/huang_xw/article/details/61942 ...
- Mybatis 复习 Mybatis 配置 Mybatis项目结构
pom.xml文件已经贴在了文末.该项目不使用mybatis的mybatis-generator-core,而是手写Entities类,DaoImpl类,CoreMapper类 其中,Entities ...
- cpp 4个类型转换
static_cast.dynamic_cast.reinterpret_cast.const_cast 之间的区别 static_cast 用法:static_cast (expression) 说 ...
- Hadoop 大数据第一天
大数据第一天 1.Hadoop生态系统 1.1 Hadoop v1.0 架构 MapReduce(用于数据计算) HDFS(用于存储数据) 1.2 Hadoop v2.0 架构 MapReduce(用 ...
- mysql数据库链接与创建
有童鞋问到说,环境搭建好了,mysql也安装了,但是就是进不去数据库,也启动不了,一直报错,那么下面这边就说下如何用Navicat链接上创建的数据库 首先 1)在xshell里进入mysql,命令是: ...