主备队列进程一个是amqqueue_process进程一个是rabbit_amqqueue_mirror_slave进程。
通知队列进程用backing_queue去实现这个工作。
run_backing_queue(QPid, Mod, Fun) ->
gen_server2:cast(QPid, {run_backing_queue, Mod, Fun}).
rabbit_mirror_queue_master.erl
%%此处AsyncCallback为run_backing_queue,实际意思为commit_by_backing_queue
init(Q, Recover, AsyncCallback) ->
{ok, BQ} = application:get_env(backing_queue_module),
%%rabbit_variable_queue
BQS = BQ:init(Q, Recover, AsyncCallback),
State = #state{gm = GM} = init_with_existing_bq(Q, BQ, BQS),
ok = gm:broadcast(GM, {depth, BQ:depth(BQS)}),
State.
rabbit_variable_queue.erl
%%起动持久化的进程,消息/索引
init(Queue, Recover, Callback) ->
init(
Queue, Recover, Callback,
fun (MsgIds, ActionTaken) ->
msgs_written_to_disk(Callback, MsgIds, ActionTaken)
end,
fun (MsgIds) -> msg_indices_written_to_disk(Callback, MsgIds) end,
fun (MsgIds) -> msgs_and_indices_written_to_disk(Callback, MsgIds) end).
%%当创建的时候会走此流程
init(#amqqueue { name = QueueName, durable = IsDurable }, new,
AsyncCallback, MsgOnDiskFun, MsgIdxOnDiskFun, MsgAndIdxOnDiskFun) →
%%初始化队列索引状态(目录文件 ),并未产生新的进程
%%何时触发的这个动作,还需要考究下.
IndexState = rabbit_queue_index:init(QueueName,
MsgIdxOnDiskFun, MsgAndIdxOnDiskFun),
init(IsDurable, IndexState, 0, 0, [],
case IsDurable of
true -> msg_store_client_init(?PERSISTENT_MSG_STORE,
MsgOnDiskFun, AsyncCallback);
false -> undefined
end,
%%消息转储的进程
msg_store_client_init(?TRANSIENT_MSG_STORE, undefined, AsyncCallback));
首先看索引相关
rabbit_queue_index.erl
队列索引是用来
重点看下这个函数
-define(TRANSIENT_MSG_STORE, msg_store_transient).
msg_store_client_init(?TRANSIENT_MSG_STORE, undefined, AsyncCallback));
msg_store_client_init(MsgStore, MsgOnDiskFun, Callback) ->
msg_store_client_init(MsgStore, rabbit_guid:gen(), MsgOnDiskFun,
Callback).
msg_store_client_init(MsgStore, Ref, MsgOnDiskFun, Callback) ->
CloseFDsFun = msg_store_close_fds_fun(MsgStore =:= ?PERSISTENT_MSG_STORE),
rabbit_msg_store:client_init(MsgStore, Ref, MsgOnDiskFun,
fun () -> Callback(?MODULE, CloseFDsFun) end).
如果使用的msg_store呢?以发送端发送消息为例.
rabbit_channel处理basic.pubish然后 deliver_to_queues
DeliveredQPids = rabbit_amqqueue:deliver(Qs, Delivery),主队列和备队列都收到发送消息请求(deliver)
主队列收到消息后deliver_or_enqueue,其中如果attempt_delivery失败后,则会准备将消息发到队列中BQS3 = BQ:publish(Message, Props, Delivered, SenderPid, Flow, BQS2),backing queue收到publish消息知道消息将要进入队列,则广播通知各个备队列gm:broadcast(GM, {publish, ChPid, Flow, MsgProps, Msg}, rabbit_basic:msg_size(Msg)),并让最终的backing queue来做处理BQ:publish(Msg, MsgProps, IsDelivered, ChPid, Flow, BQS),
publish(Msg = #basic_message { is_persistent = IsPersistent, id = MsgId },
MsgProps = #message_properties { needs_confirming = NeedsConfirming },
IsDelivered, _ChPid, _Flow,
State = #vqstate { q1 = Q1, q3 = Q3, q4 = Q4,
next_seq_id = SeqId,
in_counter = InCount,
durable = IsDurable,
unconfirmed = UC }) ->
IsPersistent1 = IsDurable andalso IsPersistent,
%%格式化消息
MsgStatus = msg_status(IsPersistent1, IsDelivered, SeqId, Msg, MsgProps),
{MsgStatus1, State1} = maybe_write_to_disk(false, false, MsgStatus, State),
State2 = case ?QUEUE:is_empty(Q3) of
false -> State1 #vqstate { q1 = ?QUEUE:in(m(MsgStatus1), Q1) };
true -> State1 #vqstate { q4 = ?QUEUE:in(m(MsgStatus1), Q4) }
end,
InCount1 = InCount + 1,
UC1 = gb_sets_maybe_insert(NeedsConfirming, MsgId, UC),
State3 = stats({1, 0}, {none, MsgStatus1},
State2#vqstate{ next_seq_id = SeqId + 1,
in_counter = InCount1,
unconfirmed = UC1 }),
a(reduce_memory_use(maybe_update_rates(State3))).
一、如何将消息写入磁盘
%%尝试将消息和索引写入到磁盘中
maybe_write_to_disk(ForceMsg, ForceIndex, MsgStatus, State) ->
{MsgStatus1, State1} = maybe_write_msg_to_disk(ForceMsg, MsgStatus, State),
maybe_write_index_to_disk(ForceIndex, MsgStatus1, State1).
maybe_write_msg_to_disk(Force, MsgStatus = #msg_status {
msg = Msg, msg_id = MsgId,
is_persistent = IsPersistent },
State = #vqstate{ msg_store_clients = MSCState,
disk_write_count = Count})
when Force orelse IsPersistent ->
case persist_to(MsgStatus) of
msg_store -> ok = msg_store_write(MSCState, IsPersistent, MsgId,
prepare_to_store(Msg)),
{MsgStatus#msg_status{msg_in_store = true},
State#vqstate{disk_write_count = Count + 1}};
queue_index -> {MsgStatus, State}
end;
(_Force, MsgStatus, State) ->
{MsgStatus, State}.
备队列
初始化主队列之后从coordinator获取gm,来实现队列进程之间的通信
init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) ->
{ok, CPid} = rabbit_mirror_queue_coordinator:start_link(
Q, undefined, sender_death_fun(), depth_fun()),
GM = rabbit_mirror_queue_coordinator:get_gm(CPid),
Self = self(),
ok = rabbit_misc:execute_mnesia_transaction(
fun () ->
[Q1 = #amqqueue{gm_pids = GMPids}]
= mnesia:read({rabbit_queue, QName}),
ok = rabbit_amqqueue:store_queue(
Q1#amqqueue{gm_pids = [{GM, Self} | GMPids],
state = live})
end),
%%获取slave节点
{_MNode, SNodes} = rabbit_mirror_queue_misc:suggested_queue_nodes(Q),
%%增加镜像队列
rabbit_mirror_queue_misc:add_mirrors(QName, SNodes, sync),
#state { name = QName,
gm = GM,
coordinator = CPid,
backing_queue = BQ,
backing_queue_state = BQS,
seen_status = dict:new(),
confirmed = [],
known_senders = sets:new() }.
add_mirrors(QName, Nodes, SyncMode) ->
[add_mirror(QName, Node, SyncMode) || Node <- Nodes],
ok.
add_mirror(QName, MirrorNode, SyncMode) ->
case rabbit_amqqueue:lookup(QName) of
{ok, Q} ->
rabbit_misc:with_exit_handler(
rabbit_misc:const(ok),
fun () ->
SPid = rabbit_amqqueue_sup_sup:start_queue_process(
MirrorNode, Q, slave) ,
log_info(QName, "Adding mirror on node ~p: ~p~n",
[MirrorNode, SPid]),
rabbit_mirror_queue_slave:go(SPid, SyncMode)
end);
{error, not_found} = E ->
E
end.
rabbit_mirror_queue_slave:go(SPid, SyncMode)
go(SPid, sync) -> gen_server2:call(SPid, go, infinity);
进程刚创建起来之后state仍是{not_started,Q}
handle_call(go, _From, {not_started, Q} = NotStarted) ->
case handle_go(Q) of
{ok, State} -> {reply, ok, State};
{error, Error} -> {stop, Error, NotStarted}
end;
handle_go
创建gm,加入group(queue_name)
rabbit_mirror_queue_slave使用 gm 和gen_server2两种behaviour,但是对于gm的使用只是实现了其中的一些回调函数。如果出现rabbit_mirror_queue_slave进程处理gm中的add_on_right等消息是错误的。
之所以主队列进程的backing queue不直接是variable queue,而是rabbit_mirror_queue_master,是因为主队列需要在对消息处理完后,需要同步给备队列,所以可以使用gm。