消息服務器使用socket,爲避免服務器過載,單臺只容許500個socket鏈接,當一臺不夠的時候,擴充消息服務器是必然,問題來了,如何讓連接在不一樣消息服務器上的用戶能夠實現消息發送呢?php
要實現消息互通就必需要讓這些消息服務器自己能互通,想了兩個方式,一種是消息服務器之間交叉連接,另外一種是增長一個特殊的消息服務器,這個消息服務器不對外開放,只負責消息轉發和推送。nginx
下列測試不考慮防火牆等。僅測試可行性和效率。redis
192.168.0.201 9501 192.168.0.202 9501
192.168.0.203 9501
Redis 192.168.0.231 6379
centos 6.5 mini swoole php
整個流程圖以下:json
流程圖說明:client1
可向client2
或者其餘client
發送消息,並接收其餘client
發送的消息.centos
Redis
中保存client
鏈接的信息,給每一個用戶分配惟一的key
,包括連接的哪臺服務器,轉發服務器定時檢測消息服務器,如消息服務器掛掉,由轉發服務器清理掉Redis已經掛掉的全部連接。緩存
完整的流程:服務器
1.Client1
給Client2
發送一條消息swoole
2.Socket1
接收到消息,根據key從Redis
取出Client2
的鏈接信息,鏈接在本機,直接推送給Client2
,流程結束。異步
3.若是鏈接不在本機,把消息推送到轉發服務器,由轉發服務器把該消息推送給鏈接所在消息服務器,消息服務器接收消息,推送給Client2
。socket
4.消息發送結束。
Socket
在socket1上建立一個server.php,內容以下:
<?php //服務端 $serv = new swoole_server("0.0.0.0", 9501); //redis $redis = new \Redis(); $redis->connect("192.168.0.231", 6379); //client $proxy = new swoole_client(SWOOLE_TCP | SWOOLE_KEEP); $proxy->connect("192.168.0.203", 9501); $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") { //須要轉發 $data['cmd'] = 'forward'; $data['recv_ip'] = $recv['socket_ip']; $serv->task(json_encode($data)); } else { //直接發送 $serv->send($recv['fd'], "{$data['send']}給您發了消息:{$data['content']}"); } break; case "forward"://接收轉發消息 $recv = unserialize($redis->get($data['recv'])); $serv->send($recv['fd'], "{$data['send']}給您發了消息:{$data['content']}"); break; } //$serv->send($fd, 'Swoole: ' . $data); }); $serv->on('task', function ($serv, $task_id, $from_id, $data) { global $proxy; $proxy->send($data); }); $serv->on('finish', function ($serv, $task_id, $data) { }); $serv->on('close', function ($serv, $fd) { echo "Client: Close.\n"; }); $serv->set(array('task_worker_num' => 4)); $serv->start();
在socket2上只需把ip變動一下便可。192.168.0.201變動爲192.168.0.202.
Proxy
在轉發服務器上創建腳本proxy.php,內容以下:
$serv = new swoole_server("0.0.0.0", 9501); //服務端 $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; $serv->task($data); }); $serv->on('task', function ($serv, $task_id, $from_id, $data) { $forward = (array) json_decode($data); $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $client->connect($forward['recv_ip'], 9501); unset($forward['recv_ip']); $client->send(json_encode($forward)); $client->close(); }); $serv->on('finish', function ($serv, $task_id, $data) { }); $serv->on('close', function ($serv, $fd) { echo "Client: Close.\n"; }); $serv->set(array('task_worker_num' => 4)); $serv->start();
注意開啓順序
1.開啓轉發服務器php proxy.php
2.分別開啓socket服務器php server.php
能夠在轉發服務器上看到兩個消息服務器已經鏈接
3.開始測試,分別打開兩個telnet,鏈接兩個消息服務器,發送消息測試:
登錄
發送消息測試
消息成功接收。
基於強大的swoole
擴展,讓php高效的實現這些成爲可能,目前消息服務器到轉發服務器是長鏈接,轉發服務器到消息服務器是短鏈接,存在性能瓶頸,也浪費了鏈接資源。下一步改形成長鏈接,消息服務器的client使用異步。