1. 程式人生 > >使用GatewayWorker框架,多個workerman程序(businessworker)負載不均衡的問題解決過程

使用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)];
    }

          修改後問題解決!