说起消息重入队列还得从队列注册消费者说起,客户端在向队列注册消费者之后,创建的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之消息重入队列的更多相关文章

  1. RabbitMQ延迟消息:死信队列 | 延迟插件 | 二合一用法+踩坑手记+最佳使用心得

    前言 前段时间写过一篇: # RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得 很多人加了我好友,说很喜欢这篇文章,也问了我一些问题. 因为最近工作比较忙, ...

  2. WinExec可能会引起消息重入

    WinExec不仅会造成延迟,并且还会引起消息的重入. 以下是调用堆栈: WinvoiceCC.exe!CWinvoiceCCDlg::OnMsgHttpReq(unsigned int wParam ...

  3. rabbitmq设置消息优先级、队列优先级配置

    1.首先在consume之前声明队列的时候,要加上x-max-priority属性,一般为0-255,大于255出错  -----配置队列优先级 配置成功后rabbitmq显示: 2.在向exchan ...

  4. RabbitMQ .NET消息队列使用入门(二)【多个队列间消息传输】

    孤独将会是人生中遇见的最大困难. 实体类: DocumentType.cs public enum DocumentType { //日志 Journal = 1, //论文 Thesis = 2, ...

  5. RabbitMQ .NET消息队列使用入门(一)【简单示例】

    首先下载安装包,我都环境是win7 64位: 去官网下载 otp_win64_19.0.exe 和rabbitmq-server-3.6.3.exe安装好 然后开始编程了: (1)创建生产者类: cl ...

  6. SpringBoot | 第三十八章:基于RabbitMQ实现消息延迟队列方案

    前言 前段时间在编写通用的消息通知服务时,由于需要实现类似通知失败时,需要延后几分钟再次进行发送,进行多次尝试后,进入定时发送机制.此机制,在原先对接银联支付时,银联的异步通知也是类似的,在第一次通知 ...

  7. C#调用RabbitMQ实现消息队列

    前言 我在刚接触使用中间件的时候,发现,中间件的使用并不是最难的,反而是中间件的下载,安装,配置才是最难的. 所以,这篇文章我们从头开始学习RabbitMq,真正的从头开始. 关于消息队列 其实消息队 ...

  8. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

  9. RabbitMQ分布式消息队列服务器(一、Windows下安装和部署)

    RabbitMQ消息队列服务器在Windows下的安装和部署-> 一.Erlang语言环境的搭建 RabbitMQ开源消息队列服务是使用Erlang语言开发的,因此我们要使用他就必须先进行Erl ...

随机推荐

  1. java26

    1:网络编程(理解)    (1)网络编程:用Java语言实现计算机间数据的信息传递和资源共享    (2)网络编程模型    (3)网络编程的三要素        A:IP地址           ...

  2. poj1001_Exponentiation_java高精度

    Exponentiation Time Limit: 500MS   Memory Limit: 10000K Total Submissions: 162918   Accepted: 39554 ...

  3. Android笔记:调试android程序

    1.Debug 第一步: 添加断点 第二步: 右击项目→Debug As→Android Application  //之后一个对话框出现,一会自动消失 第三步: 执行手机端操作,Eclipse 就会 ...

  4. android 第三方登录---新浪微博

    1.AndroidManiFest.xml设置,这里我只是简单的用授权,获取基本信息,所以只用了这一个 <!--微博--> <!-- 必须注册在微博授权,分享微博时候用到 --> ...

  5. java -- getOutputStream() has already been called for

    原文:https://my.oschina.net/zhongwenhao/blog/209653 原因:既调用了response.getOutputStream(),又调用了response.get ...

  6. tensorflow的安装

    binary安装(推荐) 注意需要能访问外网 Install pip (or pip3 for python3) if it is not already installed: # Ubuntu/Li ...

  7. eventbus实时更新

    1.发送方 EventBus.getDefault().post(new FriendApprovalEvent()); 2.接收方 /** * 收到好友消息 * * @param event */ ...

  8. HttpWatch的时间分析

    HttpWatch的时间段捕捉详解 这是一个IE的插件,下载可以点这里.下载后解压如下图所示,一共有4个文件.HttpWatch Professional是单独软件,可以单独使用. 解压后有四个文件 ...

  9. python 爬虫(一)

    1. 一次简单的网页访问 urllib 是一个标准的python库(意味着不需要安装任何附件的东西来运行这个demo),包含了通过网络请求数据的方法,处理cookies,甚至更改metadata比如h ...

  10. SOAPUI使用教程-MockServices工作原理

    在soapUI的可让您只需从WSDL基础服务创建一个基于WSDL服务的符合标准的模拟.被称为“MockService”,这可以直接从内部的soapUI运行,命令行浇道,或甚至标准servlet容器. ...