昨天初步實現了不一樣聊天服務器消息互發,但效率很差,且有性能瓶頸,Swoole的做者韓天峯提醒可使用長鏈接轉發消息,今天測試了一遍,效果不錯。php
上一篇的邏輯講的不是很清楚,此次清楚的描述下:現有A
、B
兩臺消息服務器,用戶1
鏈接在服務器A
,用戶2
鏈接在服務器B
,而用戶1
須要給用戶2
發送消息,因爲Socket
鏈接在不一樣服務器,沒法直接互通。現有兩種辦法解決:redis
1. 消息服務器進行串聯json
優勢centos
不用單獨增長服務器來作proxy
,服務器與服務器之間可酌情采用長鏈接/短連接。服務器
消息少一次傳輸,能夠減小網絡消耗。swoole
缺點網絡
每臺服務器必須和剩下的全部服務器創建鏈接。若是其中任意一個鏈接斷掉,都會影響服務,服務器多的時候也不利於監控服務狀態。異步
2. 單獨使用服務器作轉發 並聯socket
優勢分佈式
消息服務器專一於消息收發,再也不發起Client
,專一於服務端自己。
轉發服務器發起Client
鏈接全部消息服務器,利於監控服務器狀態,並且轉發服務器
宕機不影響消息服務器
,對用戶的影響時間短。
可使用keepalived
來給轉發服務器
作高可用,或者把轉發服務器作成鏈接池
,進一步減輕單臺服務器的壓力。
更能夠把一個轉發服務器和幾臺消息服務器作成一個節點,再把這些節點組合起來,能夠知足超大的即時通信服務。
缺點
因爲消息經過轉發服務器,會比直接串聯多一次數據傳輸,增長了網絡消耗。
暫時沒想到其餘的。
流程圖
環境
全部測試在虛擬機上進行:
主機
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:
開啓消息服務器2:
開啓轉發服務器
telnet登錄兩個用戶到socket1和socket2
如今用戶2給用戶1發送消息測試
成功收到消息,後面就是進行鍼對性的優化了。
消息格式
{"cmd":"login","name":"zhima201"} //登錄消息 {"cmd":"chat","send":"zhima201","recv":"zhima202","content":"how are youe"}//文本消息