上图是我以前给同事作ejabberd代码走读的时候画的一个消息流图,当时一个同事问我“ejabberd为何要设计gen_iq_handler这个模块?”我当时没回答出来,让另一个同事简单回答了一下。过后我又研究了一下代码,以为设计gen_iq_handler的缘由有如下几个?微信
1:模块配置里面有{iqdisc, Value}这一项配置,value的值能够是no_queue, one_queue, {quques, N}和parallel。gen_iq_handler里面对不一样的配置有不一样的处理代码,以下:session
add_iq_handler(Component, Host, NS, Module, Function, Type) -> case Type of no_queue -> Component:register_iq_handler(Host, NS, Module, Function, no_queue); one_queue -> {ok, Pid} = supervisor:start_child(ejabberd_iq_sup, [Host, Module, Function]), Component:register_iq_handler(Host, NS, Module, Function, {one_queue, Pid}); {queues, N} -> Pids = lists:map( fun(_) -> {ok, Pid} = supervisor:start_child( ejabberd_iq_sup, [Host, Module, Function]), Pid end, lists:seq(1, N)), Component:register_iq_handler(Host, NS, Module, Function, {queues, Pids}); parallel -> Component:register_iq_handler(Host, NS, Module, Function, parallel) end.
2:有些IQ结的业务处理很耗时,而有些IQ结的业务处理很快。spa
3:对于耗时的IQ结处理,不能把模块的iqdisc配置成no_queue,由于若是这样作的话,由下面面的代码可知,这条session链接就会被阻塞,直到这个IQ结处理完才能继续处理下一个数据包。设计
handle(Host, Module, Function, Opts, From, To, IQ) -> case Opts of no_queue -> process_iq(Host, Module, Function, From, To, IQ); {one_queue, Pid} -> Pid ! {process_iq, From, To, IQ}; {queues, Pids} -> Pid = lists:nth(erlang:phash(now(), length(Pids)), Pids), Pid ! {process_iq, From, To, IQ}; parallel -> spawn(?MODULE, process_iq, [Host, Module, Function, From, To, IQ]); _ -> todo end.
4:设计gen_iq_handler之后,主业务逻辑不须要直接访问相应的mod_功能模块,也就不须要关心这些模块的配置。主业务逻辑只须要调用gen_iq_handler提供的简单接口,全部这些功能模块的配置细节,以及如何根据这些配置细节来调用相应的功能模块,由gen_iq_handler来处理。gen_iq_handler屏蔽了各模块的调用差别。code
5:配置成one_queue, {quques, N}和parallel的这些功能模块,也不须要重复实现gen_server行为模式,由gen_iq_handler来帮他们实现。这样作还有一个好处,就是使模块能够自由的在no_queue和one_queue, {quques, N}和parallel自由切换,而不须要增长或减小任何代码,只须要修改配置。server
6:模块的增减,不须要修改主业务逻辑代码,只须要增长/减小mod_功能模块以及修改配置。接口
---------------------------------------------------------hash
欢迎关注个人微信公众号 ^_^io