基於SWOOLE的分佈式SOCKET消息服務器架構

消息服務器使用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

    enter image description here

  • 流程圖說明:
    client1可向client2或者其餘client發送消息,並接收其餘client發送的消息.centos

    Redis中保存client鏈接的信息,給每一個用戶分配惟一的key,包括連接的哪臺服務器,轉發服務器定時檢測消息服務器,如消息服務器掛掉,由轉發服務器清理掉Redis已經掛掉的全部連接。緩存

  • 完整的流程:服務器

    1.Client1Client2發送一條消息swoole

    2.Socket1接收到消息,根據key從Redis取出Client2的鏈接信息,鏈接在本機,直接推送給Client2,流程結束。異步

    3.若是鏈接不在本機,把消息推送到轉發服務器,由轉發服務器把該消息推送給鏈接所在消息服務器,消息服務器接收消息,推送給Client2socket

    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

enter image description here

能夠在轉發服務器上看到兩個消息服務器已經鏈接
3.開始測試,分別打開兩個telnet,鏈接兩個消息服務器,發送消息測試:
登錄

enter image description here

發送消息測試

enter image description here

消息成功接收。

基於強大的swoole擴展,讓php高效的實現這些成爲可能,目前消息服務器到轉發服務器是長鏈接,轉發服務器到消息服務器是短鏈接,存在性能瓶頸,也浪費了鏈接資源。下一步改形成長鏈接,消息服務器的client使用異步。

相關文章
相關標籤/搜索