上圖是我以前給同事作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