[轉]Erlang集群RPC通道擁塞問題及解決方案


原創文章,轉載請注明: 轉載自系統技術非業余研究

本文鏈接地址: Erlang集群RPC通道擁塞問題及解決方案

Erlang的集群默認情況下是全聯通的,也就是當一個節點加入集群的時候,介紹人會推薦集群里面所有的節點主動來和新加入的節點建立聯系,
效果如下圖:

erlang_connect

我們這次不講如何避免全聯通而是來講這個節點間通道的問題。

我們知道erlang的消息發送是透明的,只要調用Pid!Msg, 虛擬機和集群的基礎設施會保證消息到達指定的進程的消息隊列,這個是語義方面的保證。那么如果該Pid是在別的節點,這個消息就會通過節點間的rpc通道來傳遞。rpc模塊就是基於erlang的這個語義在上面實現了遠程函數調用。

目前社區推比較推薦erlang服務分層,所以層和層之間的交互基本上透過rpc來進行的。類似下圖的分層結構越來越多,當大量的消息在節點間流動的話,勢必會造成通道擁塞。

layer

阻塞會導致發送進程被掛起,而rpc是單進程(gen_server)的,被掛起,rpc調用就廢了。當然除了RPC, Pid!Msg 這種方式還是可以並行的走的。
這種阻塞極大的影響力系統的rt, 對性能和體驗有很大的影響。

那這個問題如何定位、解決呢?Erlang很貼心的提供了一攬子解決方案:

首先是發現問題:

erlang:system_monitor(MonitorPid, Options) -> MonSettings

busy_dist_port
If a process in the system gets suspended because it sends to a process on a remote node whose inter-node communication was handled by a busy port, a message {monitor, SusPid, busy_dist_port, Port} is sent to MonitorPid. SusPid is the pid that got suspended when sending through the inter-node communication port Port.

比如說 riak_sysmon 就用了以下代碼:

BusyDistPortP = get_busy_dist_port(),
   Opts lists:flatten(
            [[{long_gc, GcMsLimit} || lists:member(gc, MonitorProps)
                                          andalso GcMsLimit > 0],
             [{large_heap, HeapWordLimit} || lists:member(heap, MonitorProps)
                                                 andalso HeapWordLimit > 0],
             [busy_port || lists:member(port, MonitorProps)
                               andalso BusyPortP],
             [busy_dist_port || lists:member(dist_port, MonitorProps)
                                    andalso BusyDistPortP]]),
   _ = erlang:system_monitor(self(), Opts),

當我們收到{monitor, SusPid, busy_dist_port, Port}消息的時候,就可以確認系統經常有阻塞問題。

那么如何解決呢?

社區早就認識到這個問題, 所以設計dist_buf_busy_limit是個可配置的值。

我們需要先知道我們的系統是什么情況:

從 http://www.erlang.org/doc/man/erlang.html#system_info_dist_buf_busy_limit 摘抄如下:

erlang:system_info(Item :: dist_buf_busy_limit) -> integer() >= 0
dist_buf_busy_limit
Returns the value of the distribution buffer busy limit in bytes. This limit can be set on startup by passing the +zdbbl command line flag to erl.

我們來演示下:

$ erl
Erlang R16B (erts-5.10.1) 1 [64-bit] [smp:16:16] [async-threads:10] [hipe] [kernel-poll:false]
 
Eshell V5.10.1  (abort with ^G)
1> erlang:system_info(dist_buf_busy_limit).
1048576
2>

默認情況下是1M,一般情況下是夠用了,但是如果你的rpc沒設計好返回大量的數據,這個值就可能不夠了。

我們可以通過修改這個值來回避這個問題:

從 http://www.erlang.org/doc/man/erl.html#+zdbbl 摘抄如下:

+zdbbl size
Set the distribution buffer busy limit (dist_buf_busy_limit) in kilobytes. Valid range is 1-2097151. Default is 1024.

A larger buffer limit will allow processes to buffer more outgoing messages over the distribution. When the buffer limit has been reached, sending processes will be suspended until the buffer size has shrunk. The buffer limit is per distribution channel. A higher limit will give lower latency and higher throughput at the expense of higher memory usage.

需要注意的是它的單位是K。

社區也碰到很多這樣的問題,比較典型的就是riak自己,參看這篇 文章

The important takeaway here is to check your Riak logs for busy_dist_port warnings and take them seriously.

A simple addition of the line +zdbbl 8192 to Riak’s vm.args to bump the Erlang distribution channel buffer size to 8MB from the default of 1MB is all it took to realize all of the benefits discussed thus far.

小結: 發現問題,解決問題,需要有測量和數據。

祝玩得開心!


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com