swoole TCP 集群 MQTT消息通信服务器

本文介绍了使用Swoole和Redis构建分布式消息服务器的方案,当单台服务器连接数达到上限时,通过扩展消息服务器并设置转发服务器实现用户间消息的互通。转发服务器作为中介,处理不同服务器之间的消息传递。同时,提出了利用Redis的订阅发布模型优化通信的思路,并提到了结合阿里云RocketMQ的潜在改进方案。
摘要由CSDN通过智能技术生成

消息服务器使用socket,为避免服务器过载,单台只允许500个socket连接,当一台不够的时候,扩充消息服务器是必然,问题来了,如何让链接在不同消息服务器上的用户可以实现消息发送呢?

要实现消息互通就必须要让这些消息服务器本身能互通,想了两个方式,一种是消息服务器之间交叉链接,另一种是增加一个特殊的消息服务器,这个消息服务器不对外开放,只负责消息转发和推送。

下列测试不考虑防火墙等。仅测试可行性和效率。

测试环境

消息服务器

ip地址:192.168.0.201 端口:9501 
ip地址:192.168.0.202 端口:9501

转发服务器

ip地址:192.168.0.203  端口:9501

公共缓存(redis)

ip地址:192.168.0.231  端口:6379

软件环境

centos 6.5 mini swoole php

流程说明

  • client1(服务器1 - 用户user)可向client2(服务器2 - 用户user)或者其他client(服务器 - 用户user)发送消息,并接收其他client(服务器 - 用户user)发送的消息。
  • Redis中保存client(服务器 - 用户user)连接的信息,给每个用户分配唯一的key,包括链接的哪台服务器,转发服务器定时检测消息服务器,如消息服务器挂掉,由转发服务器清理掉Redis已经挂掉的所有链接。

完整流程

  1. Client1给Client2发送一条消息
  2. Socket1接收到消息,根据key从Redis取出Client2的连接信息,连接在本机,直接推送给Client2,流程结束。
  3. 如果连接不在本机,把消息推送到转发服务器,由转发服务器把该消息推送给连接所在消息服务器,消息服务器接收消息,推送给Client2。
  4. 消息发送结束。

编程实现

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使用异步。

redis sub/pub 模型

原理
使用redis发布订阅模型实现中间件消息通信。

改进版

swoole + aliyun - 消息队列 RocketMQ 版
模型图
在这里插入图片描述

原文链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值