zoukankan      html  css  js  c++  java
  • Swoole 中使用 TCP 异步服务器、TCP 协程服务器、TCP 同步客户端、TCP 协程客户端

    TCP 异步风格服务器

    异步风格服务器通过监听事件的方式来编写程序。当对应的事件发生时底层会主动回调指定的函数。

    由于默认开启协程化,在回调函数内部会自动创建协程,遇到 IO 会产生协程调度,异步风格服务器无法保证调度顺序,所以在遇到并发时无法保证事件执行顺序。

    # server.php
    
    // 创建 TCP 服务器对象,监听 0.0.0.0:9501端口
    $serv = new SwooleServer("0.0.0.0", 9501); 
    
    // 设置服务器运行参数
    $serv->set(array(
        'daemonize'     => 1,  // 作为守护进程运行,需同时设置log_file
        'log_file'      => '/www/logs/swoole.log',  // 指定标准输出和错误日志写入的文件
    ));
    
    // 监听连接进入事件
    // $fd 为客户端连接的唯一标识符
    $serv->on('Connect', function ($serv, $fd) {
        echo "Client: Connect: {$fd} .
    ";
    });
    
    // 监听数据接收事件
    $serv->on('Receive', function ($serv, $fd, $from_id, $data) {
        // 向指定的客户端连接发送数据
        $serv->send($fd, "Server: " . $data);
    });
    
    // 监听连接关闭事件
    $serv->on('Close', function ($serv, $fd) {
        echo "Client: Close: {$fd} .
    ";
    });
    
    // 监听服务器正常关闭事件
    // 使用 kill -15 发送 SIGTREM 信号到主进程正常关闭时触发
    // kill -9 或 Ctrl+C 不会触发该事件
    $serv->on('Shutdown', function ($serv) {
        echo "服务器正常关闭.
    ";
    });
    
    // 启动服务器
    $serv->start(); 
    

    运行并测试 TCP 异步风格服务器

    # 如果程序已经运行,先结束进程
    kill -9 11590
    
    # 在 cli 命令行环境运行服务端
    php server.php
    
    # 查看服务器监听的端口
    netstat -an | grep 9501
    
    # 使用Telnet测试连接服务端
    telnet 127.0.0.1 9501
    # 发送数据
    hello
    # 接收数据
    Server: hello
    

    TCP 协程风格服务器

    协程风格服务器处理连接的过程是完全同步的,程序可以顺序处理 ConnectReceiveClose 事件,可以保证事件执行顺序。

    由于 TCP 协程风格服务器不支持设置工作进程数,服务器每次只能处理一个连接请求,如果请求中包含耗时逻辑,会严重影响并发性能,所以需要借助进程池实现多核CPU的利用。

    # server.php
    
    // 在进程池中创建两个进程
    $pool = new SwooleProcessPool(2);
    
    $pool->set([
        'enable_coroutine' => true,  // 让每个 OnWorkerStart 回调都自动创建一个协程
        'daemonize'     => 1,  // 作为守护进程运行,需同时设置log_file
        'log_file'      => '/www/logs/swoole.log',  // 指定标准输出和错误日志写入的文件
    ]);
    
    // 进程绑定工作进程启动事件
    $pool->on('workerStart', function ($pool, $id) {
        // 每个进程都监听9501端口,不使用ssl,开启端口重用
        $server = new SwooleCoroutineServer('0.0.0.0', '9501', false, true);
    
        // 接收 kill -15 信号关闭服务器
        SwooleProcess::signal(SIGTERM, function () use ($server) {
            echo "服务器正常关闭.
    ";
            $server->shutdown();
        });
    
        // 设置连接处理函数,接收到新的连接请求并自动创建一个协程并执行回调函数
        // 回调函数会在协程空间中执行
        $server->handle(function(SwooleCoroutineServerConnection $conn) {
            echo "Client: Connect.
    ";
            
            // 循环接收和返回数据
            while (true) {
                // 接收数据
                $data = $conn->recv();
                if (empty($data)) {
                    // 关闭连接
                    $conn->close();
                    break;
                }
    
                //发送数据
                $conn->send("Server: " . $data);
                Co::sleep(1);
            }
        });
    
        // 启动服务器
        $server->start();
    });
    $pool->start();
    

    由于TCP 协程风格服务器同一时间只能处理一个连接,为了充分利用CPU多核、处理多个连接,需要用到Swoole中的多进程模型+端口重用。

    TCP 同步阻塞客户端

    // 同步阻塞客户端可以用于 PHP-FPM 环境下
    $client = new SwooleClient(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
    if (!$client->connect('127.0.0.1', 9501, -1)) {
        exit("connect failed. Error: {$client->errCode}
    ");
    }
    
    $client->send("hello world
    ");
    
    // 同步阻塞等待服务端返回内容
    echo $client->recv();
    
    $client->close();
    

    TCP 协程客户端

    // 设置要 Hook 的函数的范围
    Co::set(['hook_flags'=> SWOOLE_HOOK_ALL]);
    
    // 协程客户端,底层自动使用协程调度实现异步IO,用于代替异步客户端
    Co
    un(function(){
      $client = new SwooleCoroutineClient(SWOOLE_SOCK_TCP);
      
      $client->set(array(
        'timeout' => 1.5,          //总超时,包括连接、发送、接收所有超时
        'connect_timeout' => 1.0,  //连接超时,会覆盖第一个总的 timeout
        'write_timeout' => 2.0,    //发送超时,会覆盖第一个总的 timeout
        'read_timeout' => 0.5,     //接收超时,会覆盖第一个总的 timeout
      ));
      
      if (!$client->connect('127.0.0.1', 9501, 0.5)) {
          exit("connect failed. Error: {$client->errCode}
    ");
      }
      
      $client->send("hello world.
    ");
      
      echo $client->recv();
      
      $client->close();
    });
    
  • 相关阅读:
    SSH防止超时的设置
    vuejs中v-bind绑定class时的注意事项
    js常用到的方法积累
    主流浏览器以及系统的判断
    理解Javascript的状态容器Redux
    可伸缩架构简短系列
    在Node.js中使用RabbitMQ系列一 Hello world
    在Node.js中使用RabbitMQ系列二 任务队列
    Javascript中的async await
    公钥,私钥和数字签名
  • 原文地址:https://www.cnblogs.com/danhuang/p/13192905.html
Copyright © 2011-2022 走看看