基於SWOOLE的高可用分佈式SOKET服務器系統搭建V2.0

基於SWOOLE的高可用分佈式SOKET服務器系統搭建V2.0

  • 做者: hope
  •  
  • 時間: October 30, 2014
  •  
  • 分類: php

昨天初步實現了不一樣聊天服務器消息互發,但效率很差,且有性能瓶頸,Swoole的做者韓天峯提醒可使用長鏈接轉發消息,今天測試了一遍,效果不錯。php

上一篇的邏輯講的不是很清楚,此次清楚的描述下:現有AB兩臺消息服務器,用戶1鏈接在服務器A用戶2鏈接在服務器B,而用戶1須要給用戶2發送消息,因爲Socket鏈接在不一樣服務器,沒法直接互通。現有兩種辦法解決:redis

1. 消息服務器進行串聯json

  • 優勢centos

    不用單獨增長服務器來作proxy,服務器與服務器之間可酌情采用長鏈接/短連接服務器

    消息少一次傳輸,能夠減小網絡消耗。swoole

  • 缺點網絡

    每臺服務器必須和剩下的全部服務器創建鏈接。若是其中任意一個鏈接斷掉,都會影響服務,服務器多的時候也不利於監控服務狀態異步

2. 單獨使用服務器作轉發 並聯socket

  • 優勢分佈式

    消息服務器專一於消息收發,再也不發起Client,專一於服務端自己。

    轉發服務器發起Client鏈接全部消息服務器,利於監控服務器狀態,並且轉發服務器宕機不影響消息服務器,對用戶的影響時間短。

    可使用keepalived來給轉發服務器作高可用,或者把轉發服務器作成鏈接池,進一步減輕單臺服務器的壓力。

    更能夠把一個轉發服務器和幾臺消息服務器作成一個節點,再把這些節點組合起來,能夠知足超大的即時通信服務。

  • 缺點

    因爲消息經過轉發服務器,會比直接串聯多一次數據傳輸,增長了網絡消耗。

    暫時沒想到其餘的。

開始搭建

  • 流程圖

    enter image description here

  • 環境

    全部測試在虛擬機上進行:

    • 主機

      os  Ubuntu14.10 64位
      cpu 4 core
      mem 16 G
      soft Virtual box
    • 虛擬機

      centos 6.5 mini swoole php//
      192.168.0.201   //socket1 消息服務器
      192.168.0.202   //socket2 消息服務器
      192.168.0.203   //proxy   轉發服務器
      192.168.0.231   //Redis
    • 客戶端

      telnet

編碼

  • 消息服務器

    因爲消息服務器再也不發起客戶端鏈接,因此代碼要精簡不少。直接上測試代碼:

    <?php
    
    /**
     * @filename server.php
     * @encoding UTF-8
     * @author CaiXin Liao <king.liao@qq.com>
     * @link http://www.51zhima.com
     * @copyright 51zhima@CopyRight
     * @datetime 2014-10-28 15:09:54
     * @version 1.0
     * @Description 
     */
    //服務端
    $serv = new swoole_server("0.0.0.0", 9501);
    
    //redis
    $redis = new \Redis();
    $redis->connect("192.168.0.231", 6379);
    
    //Server
    $serv->on('start', function($serv) {
        echo "Service:Start...";
    });
    $serv->on('connect', function ($serv, $fd) {
    
    });
    $serv->on('receive', function ($serv, $fd, $from_id, $data) {
        global $redis;
    
        $data = (array) json_decode($data);
        $cmd = $data['cmd'];
    
        switch ($cmd) {
    
            case "login"://登錄
                //保存鏈接信息
                $save = array(
                    'fd' => $fd,
                    'socket_ip' => "192.168.0.201"
                );
                $redis->set($data['name'], serialize($save));
                break;
    
            case "chat":
                $recv = unserialize($redis->get($data['recv']));
                if ($recv['socket_ip'] != "192.168.0.201") {//發消息給proxy
                   $proxy = unserialize($redis->get('192.168.0.201_router'));
                    //須要轉發
                    $data['recv_ip'] = $recv['socket_ip'];
                    $serv->send($proxy['fd'], json_encode($data));
                } else {
                    //直接發送
                    $serv->send($recv['fd'], "{$data['send']}給您發了消息:{$data['content']}\n");
                }
                break;
        }
    });
    $serv->on('close', function ($serv, $fd) {
        echo "Client: Close.\n";
    });
    
    $serv->start();

    不一樣的消息服務器只需修改ip便可。

  • 轉發服務器

    轉發服務器須要遍歷鏈接全部的消息服務器,而且採用異步的方式,測試代碼:

    <?php
    
    /**
     * @filename proxy.php
     * @encoding UTF-8
     * @author CaiXin Liao <king.liao@qq.com>
     * @link http://www.51zhima.com
     * @copyright 51zhima@CopyRight
     * @datetime 2014-10-29 15:50:37
     * @version 1.0
     * @Description 
     */
    
    $clients = array();
    $servers = array(
        '192.168.0.201',
        '192.168.0.202',
    );
    for ($i = 0; $i < count($servers); $i++) {
        $clients[$servers[$i]] = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
        $clients[$servers[$i]]->remote_ip = $servers[$i];
        $clients[$servers[$i]]->on("connect", function(swoole_client $cli) {
            $data = array(
                'cmd'=>'login',
                'name'=>$cli->remote_ip . '_router',
            );
            $cli->send(json_encode($data));
            echo $cli->remote_ip . " Connect Success \n";
        });
    
        $clients[$servers[$i]]->on("receive", function (swoole_client $cli, $data) {
            $msg = (array) json_decode($data);
            $remote_ip = $msg['recv_ip'];
            unset($msg['recv_ip']);
            global $clients;
            $clients[$remote_ip]->send(json_encode($msg));
        });
        $clients[$servers[$i]]->on("error", function(swoole_client $cli) {
            echo "{$cli->remote_ip} error\n";
        });
    
        $clients[$servers[$i]]->on("close", function(swoole_client $cli) {
            echo "{$cli->remote_ip} Connection close\n";
        });
        $clients[$servers[$i]]->connect($servers[$i], 9501, 0.5);
    }

測試

開啓全部的虛擬機,同步腳本,這裏在proxy腳本裏沒有作監控和鏈接狀態檢測,這裏只作簡單的消息發送測試。

  • 開啓消息服務器1:

    enter image description here

  • 開啓消息服務器2:

    enter image description here

  • 開啓轉發服務器

    enter image description here

  • telnet登錄兩個用戶到socket1和socket2

    enter image description here

  • 如今用戶2給用戶1發送消息測試

    enter image description here

    成功收到消息,後面就是進行鍼對性的優化了。

  • 消息格式

    {"cmd":"login","name":"zhima201"} //登錄消息
    {"cmd":"chat","send":"zhima201","recv":"zhima202","content":"how are youe"}//文本消息
相關文章
相關標籤/搜索