使用GatewayWorker框架,多個workerman程序(businessworker)負載不均衡的問題解決過程
公司搭建一套智慧社群、智慧對講管控雲平臺時,使用GatewayWorker框架搭建app端外推送的服務。發現效能比預期的低。也就是GatewayWorker(https://github.com/walkor/GatewayWorker/)中的workerman元件往google fcm 服務和ios apns服務推送訊息的效能不及預期。經過netstat -lanp後發現只有其中一個workerman程序的Recv-Q有堆積資料,表明只有該程序收到來自gateway元件的網路訊息堆積來不及處理,其他幾個workerman程序不會。是什麼原因造成的呢?
經過一番定位,通過在workerman元件的onMessage回撥函式中增加日誌:打印出workerman自己的程序id,發現每次都是其中一個程序id而已,也就是說gateway將客戶端的訊息全部推送給這一個workerman程序,並沒有對多個業務workerman程序做負載均衡。進過分析原始碼發現在:\GatewayWorker\vendor\workerman\gateway-worker\src\Gateway.php檔案中的class Gateway建構函式如下:
/** * 建構函式 * * @param string $socket_name * @param array $context_option */ public function __construct($socket_name, $context_option = array()) { parent::__construct($socket_name, $context_option); $this->_gatewayPort = substr(strrchr($socket_name,':'),1); //added by chenyc,更改負載均衡的方式。由routerBind改成routerRand.因為routerBind的路由選擇是根據client_id來的, //akcs的業務推送客戶端一般只有一個,所以出現負載不均衡的問題 $this->router = array("\\GatewayWorker\\Gateway", 'routerRand'); $backrace = debug_backtrace(); $this->_autoloadRootPath = dirname($backrace[0]['file']); }
原先gateway的負載均衡策略是routerBind(上述的程式碼已經修改過來了,看修改記錄),相應的函式定義如下:
/** * client_id 與 worker 繫結 * * @param array $worker_connections * @param TcpConnection $client_connection * @param int $cmd * @param mixed $buffer * @return TcpConnection */ public static function routerBind($worker_connections, $client_connection, $cmd, $buffer) { if (!isset($client_connection->businessworker_address) || !isset($worker_connections[$client_connection->businessworker_address])) { $client_connection->businessworker_address = array_rand($worker_connections); } return $worker_connections[$client_connection->businessworker_address]; }
即負載均衡演算法跟客戶端的client_id繫結的,對於我們的業務,推送客戶端就是我們的業務邏輯伺服器,當前只有一臺,所以每次負載均衡演算法都對映到唯一的一個workerman程序,導致上面提出的現象。
問題原因查出來了,修改就很簡單,框架也有另一個負載均衡演算法,是每次客戶端訊息過來,gateway隨機選擇一個workerman程序來處理,對應的演算法是routerRand,定義如下:
/**
* 隨機路由,返回 worker connection 物件
*
* @param array $worker_connections
* @param TcpConnection $client_connection
* @param int $cmd
* @param mixed $buffer
* @return TcpConnection
*/
public static function routerRand($worker_connections, $client_connection, $cmd, $buffer)
{
return $worker_connections[array_rand($worker_connections)];
}
修改後問題解決!