如何使用gateway同时既做客户端又做服务端

wokerman

[attach]1348[/attach]
问题一、如上图,本地服务器 B需要接收来自客户端client  A的数据,然后将数据处理以后再发送给云服务器 C,C端处理数据以后再将处理的数据发送给B,B通过可以再次处理,发送给一个或者多个A端,
这三点直接都是长连接,请问使用gateway可以做到么,如果可以做到需要怎么配置,C端使用gateway没有问题,就是B端的gateway应该怎么使用,
问题二、B端使用两个服务器做分布式部署,请问可以在两个服务器中运行register,gateWay以及businessWorker,当一个服务器出现问题的时候,另一个服务器可以立即顶上,请问这种可是实施么,如果可以,怎么实施
谢谢大神
 

3462 9 1
9个回答

walkor

问题1,在B上可以利用asyncTcpConnection发起连接连到C,可以asyncTcpConnection发起连接然后可以send数据,asyncTcpConnection的onMessage里处理C的返回。
问题2、分布式部署故障迁移一般只需要考虑客户端到gateway部分,businessworker服务器故障了会自动下线,不影响业务。但是gateway前面最好加一层负载均衡,比如lvs

  • wokerman 2018-11-06

    好的,我现在试一下,谢谢大神@1

  • wokerman 2018-11-07

    在$con->onMessage方法中调用Gateway::sendToClient()方法没有作用。请问大神这样使用有什么特殊的地方要规避的么?谢谢大神

wokerman

Gateway框架的Events.php为何不能如下使用:

    /**
    * 当客户端发来消息时触发
    * @param int $client_id 连接id
    * @param mixed $message 具体消息
    */
   public static function onMessage($client_id, $message)
   {
        $gate = new Gateway();
        $con = new AsyncTcpConnection('tcp://192.168.1.107:8282');
        echo json_encode($con);
        $con -> onConnect = function ()use($con){
            $con->send('this is from BBB \r\n');
        };
        $con->onMessage = function ($con,$msg)use($gate){
            global $client_id;
            _**$gate::sendToClient($client_id, "this is from CCC said $msg\r\n");**_        };
        $con->connect();
   }

在$con->onMessage方法中调用Gateway::sendToClient()方法没有作用。请问大神这样使用有什么特殊的地方要规避的么?谢谢大神

  • 暂无评论
walkor

你要自己打日志,看下运行到哪里了,没有现场环境很难定位哪里问题的。
比如$con->onMessage可能根本就没运行,自然就发不出去。另外gateway不用初始化,看下手册用法
 

  • wokerman 2018-11-07

    $con->onMessage是运行了的,我在这个方法中打印数据是打印出来了的,但是调用Gateway::sendToClient(),A客户端没有反应

wokerman

[attach]1358[/attach]
下面是输出图

[attach]1359[/attach]
 

  • 暂无评论
walkor

你这个client_id放到全局变量里了,这个变量有可能会被其它请求更改,所以达不到你要的效果。
public static function onMessage($client_id, $message)
{
$con = new AsyncTcpConnection('tcp://192.168.1.107:8282');
echo json_encode($con);
$con -> onConnect = function ($con){
$con->send('this is from BBB \r\n');
};
$con->onMessage = function ($con,$msg)use($client_id){
echo "send to $client_id\n";
var_export(Gateway::isOnline($client_id));
Gateway::sendToClient($client_id, "this is from CCC said $msg\r\n");
$con->close();
};
$con->connect();

另外记得及时关闭连接(最好加个定时器关闭),否则触发多少请求就建立多少个连接,当超过服务器限制会导致服务异常。
Gateway::isOnline($client_id)能排查是不是客户端连接断开了,断开了就肯定无法发到了。
另外也可以抓包看下服务端到底发过去没,有可能是客户端bug导致以为没有发出去
 

  • wokerman 2018-11-07

    谢谢大神的指教,这个问题解决了,原因可能是使用全局的时候,$client_id再下次触发onMessage的时候为NULL了,解决方法就是在使用的时候不需要全局,写这个方法的时候use($client_id)就没问题了,麻烦大神了。我把可以使用的代码写在最后面,以供其他人使用。

wokerman

在打印Gateway类库中发现$address的地址为0.0.0.0:,发送的数据$gateway_data中的connection_id会变成null,如下图所示。

[attach]1360[/attach]
 

  • 暂无评论
walkor

作为客户端,ip地址不能为0.0.0.0

  • 暂无评论
wokerman

下面的代码是B端的代码,C端的代码无需太大改变<?php
/**

/**

  • 用于检测业务代码死循环或者长时间阻塞等问题
  • 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
  • 然后观察一段时间workerman.log看是否有process_timeout异常
    */
    //declare(ticks=1);

use \GatewayWorker\Lib\Gateway;
use \Workerman\Connection\AsyncTcpConnection;

/**

  • 主逻辑
  • 主要是处理 onConnect onMessage onClose 三个方法
  • onConnect 和 onClose 如果不需要可以不用实现并删除
    */
    class Events
    {
    private static $con = null;
    public static function getCon()
    {
    if(self::$con){
    return self::$con;
    }else{
    return new AsyncTcpConnection('ws://192.168.1.107:8282');
    }
    }
    /**

    • 当客户端连接时触发
    • 如果业务不需此回调可以删除onConnect
    • @param int $client_id 连接id
      */
      public static function onConnect($client_id)
      {
      // 向当前client_id发送数据
      Gateway::sendToClient($client_id, "Hello $client_id\r\n");
      // 向所有人发送
      Gateway::sendToAll("$client_id login\r\n");
      }

    /**

    • 当客户端发来消息时触发
    • @param int $client_id 连接id
    • @param mixed $message 具体消息
      */
      public static function onMessage($client_id, $message)
      {
      $con = self::getCon();
      $con -> onConnect = function ()use($con){
      $con->send('this is from BBB \r\n');
      };
      $con->onMessage = function ($con,$msg)use($client_id){
      Gateway::sendToClient($client_id, "this is from CCC said $msg\r\n");
      };
      $con->connect();

    }

    /**

    • 当用户断开连接时触发
    • @param int $client_id 连接id
      */
      public static function onClose($client_id)
      {
      // 向所有人发送
      GateWay::sendToAll("$client_id logout\r\n");
      }
      }
  • 暂无评论
walkor

不对,你这样是$con是一个全局的类,但是每次有请求就重置了$con->onMessage逻辑里的$client_id,最终发送会发送给最后一个,也达不到你要的效果。
保持长连接后要每个消息都传递属于哪个client_id的处理结果,否则会混乱
 
<?php
/**

/**

  • 用于检测业务代码死循环或者长时间阻塞等问题
  • 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
  • 然后观察一段时间workerman.log看是否有process_timeout异常
    */
    //declare(ticks=1);

use \GatewayWorker\Lib\Gateway;
use \Workerman\Connection\AsyncTcpConnection;

/**

  • 主逻辑
  • 主要是处理 onConnect onMessage onClose 三个方法
  • onConnect 和 onClose 如果不需要可以不用实现并删除
    */
    class Events
    {
    public static $con = null;
    public static function getCon()
    {
    if(self::$con){
    return self::$con;
    }else{
    $con = new AsyncTcpConnection('ws://192.168.1.107:8282');
    // 这里要求8282端口必须回传client_id,告诉Events这个是哪个client_id的结果
    $con->onMessage = function ($con,$msg){
    $data = json_decode($msg, true);
    $client_id = $data;
    Gateway::sendToClient($client_id, "this is from CCC said $msg\r\n");
    };
    // 连接关闭了要把Events::$con 置空,否则消息永远发不出去
    $con->close = function($con){
    Events::$con = null;
    };
    $con->connect();
    }
    }
    /**

    • 当客户端连接时触发
    • 如果业务不需此回调可以删除onConnect
    • @param int $client_id 连接id
      */
      public static function onConnect($client_id)
      {
      // 向当前client_id发送数据
      Gateway::sendToClient($client_id, "Hello $client_id\r\n");
      // 向所有人发送
      Gateway::sendToAll("$client_id login\r\n");
      }

    /**

    • 当客户端发来消息时触发
    • @param int $client_id 连接id
    • @param mixed $message 具体消息
      */
      public static function onMessage($client_id, $message)
      {
      $con = self::getCon();

      $con->send(json_encode());
      }

    /**

    • 当用户断开连接时触发
    • @param int $client_id 连接id
      */
      public static function onClose($client_id)
      {
      // 向所有人发送
      GateWay::sendToAll("$client_id logout\r\n");
      }
      }
  • wokerman 2018-11-07

    我又增加一台服务器当作A客户端,测试了以下,发现没有出现您说的这个问题,两个客户端都可以正确的接收到各个不同的传递过来的数据。谢谢大神给了另一种思路。我换您这个思路试一下。

年代过于久远,无法发表回答
🔝