1. 程式人生 > >系統技術非業餘研究 » 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.

 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)  [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.

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

祝玩得開心!

Post Footer automatically generated by wp-posturl plugin for wordpress.