Swoole的TCP服务专家 | 高效的Swoole TCP服务器解决方案

silverwq
2022-10-13 / 0 评论 / 290 阅读 / 正在检测是否收录...

tcp概述

TCP(Transmission Control Protocol)又叫传输控制协议,TCP是面向连接的,并且是一种可靠的协议,在基于TCP进行通信时,通信双方需要建立TCP连接,建立连接需要经过三次握手,握手成功才可以通信。TCP 是解决了 UDP 的顺序和丢包这两个重要的问题,它是会相互建立连接的。
优点: 可靠,稳定。TCP的可靠性体现在传输数据之前,三次握手建立连接(四次挥手断开连接),并且在数据传递时,有确认,窗口,重传,拥塞控制机制,数据传完之后断开连接来节省系统资源。
应用场景: 对网络通信质量有要求时,比如:整个数据要准确无误的传递给对方,这往往对于一些要求可靠的应用,比如HTTP,HTTPS,FTP等传输文件的协议,POP,SMTP等邮件的传输协议,websocket协议等。

swoole tcp服务

入门程序代码

$server = new Swoole\Server('127.0.0.1', 9502);
// 启动后会有6个进程:主进程和管理进程,是默认都有的
$server->set([
    'worker_num' => 2, // 2个work进程,用于处理正常的业务
    'task_worker_num' => 3, // 2个task进程,用于处理例如发送邮件啥耗时的工作
]);
// 启动后在主进程(master)的主线程回调此函数
$server->on('start', function ($server) {
    // 在此事件之前 Server 已进行了如下操作
    // 1. 启动创建完成 Manager 进程
    // 2. 启动创建完成 Worker 子进程
    // 3. 监听所有 TCP/UDP/unixSocket 端口,但未开始 Accept 连接和请求
    // 4. 监听了定时器
    echo 'onStart-> main进程 启动', PHP_EOL;
    // 只能在 onStart/onWorkerStart 之后获取到
    echo "服务器的主进程PID:{$server->master_pid}", PHP_EOL; // 主进程id,相当于
    // 只能在 onStart/onWorkerStart 之后获取到
    echo "服务器管理进程PID:{$server->manager_pid}", PHP_EOL; // 管理进程id,相当于管理fpm的master进程

    echo PHP_EOL;
});

// 此事件在 Worker 进程 / Task 进程 启动时发生,这里创建的对象可以在进程生命周期内使用。
// onWorkerStart/onStart 是并发执行的,没有先后顺序,不过好像每次都是onStart在前面
$server->on('WorkerStart', function ($server, int $workerId) {
    // $workerId:是Worker 进程 id(非进程的 PID)

    echo "onWorkerStart-> worker进程 启动", PHP_EOL;
    echo "当前Worker进程的PID:{$server->worker_pid}", PHP_EOL;
    echo "Worker进程ID:{$workerId},范围是从0开始的", PHP_EOL;
    // 当前进程是否是 Task 进程
    if ($server->taskworker == true) {
        echo "当前进程类型:当前是task进程", PHP_EOL;
    } else {
        echo "当前进程类型:当前是worker进程", PHP_EOL;
    }

    echo PHP_EOL;
});

// 有新的连接进入时,在 worker 进程中回调,比如:telnet 127.0.0.1 9502
$server->on('Connect', function ($server, $fd, $reactorId) {
    echo "onConnect->客户端连接", PHP_EOL;
    echo "文件描述符fd是:{$fd}", PHP_EOL;
    echo "连接所在的Reactor线程ID是:{$reactorId}", PHP_EOL;
    echo PHP_EOL;
});

$process = new Swoole\Process(function ($process) use ($server) {
    $socket = $process->exportSocket();
    while (true) {
        $message = "Your data is :" . $socket->recv();
        foreach ($server->connections as $conn) {
            $server->send($conn, $message);
        }
    }
}, false, 2, 1);
// 接收到数据时回调此函数,发生在 worker 进程中被调用
$server->on('Receive', function ($server, $fd, $reactorId, $data) use ($process) {
    echo "onReceive->服务端 接收消息", PHP_EOL;
    echo "文件描述符fd是:{$fd}", PHP_EOL;
    echo "连接所在的Reactor线程ID是:{$reactorId}", PHP_EOL;
    echo "Worker进程ID:{$server->worker_id}", PHP_EOL;
    echo "收到的数据内容,可能是文本或者二进制内容:{$data}", PHP_EOL;

    $server->tick(1000, function () use ($server, $fd) {
        // $server->send($fd, "hello world");
    });

    // $socket = $process->exportSocket();
    // $socket->send($data);
    // $server->send($fd,"Server receive-data:{$data}.reactor-is {$reactor_id} \n");
    // $clientInfo=$server->getClientInfo($fd);
    // var dump($clientInfo);
    if (trim($data) == 'task') {
        // 向task进程投递任务,具体是哪个进程,系统分配
        $server->task("async task coming");
    } else {
        $workerIds = [0, 1, 2, 3];
        foreach($workerIds as &$val){
            if ($val == $server->worker_id) {
                // 不能向自己发送消息
                unset($val);
            }
        }
        $worker_id = array_rand($workerIds,1);
        // 该方法,向任意 Worker 进程或者 Task 进程发送消息。在非主进程和管理进程中可调用。
        // 收到消息的进程会触发 onPipeMessage 事件
        $server->sendMessage("hello task process", $worker_id);
    }
    echo PHP_EOL;
});
// 在 task 进程内被调用
// worker 进程可以使用 task 函数向 task_worker 进程投递新的任务
// 当前的 Task 进程在调用 onTask 回调函数时会将进程状态切换为忙碌
// 这时将不再接收新的 Task,当 onTask 函数返回时会将进程状态切换为空闲然后继续接收新的 Task。
$server->on('task', function ($server, $task_id, $src_worker_id, $data) {
    // 
    echo "onTask-> task进程接收到消息",PHP_EOL;
    echo "执行任务的 task 进程 id:{$task_id}",PHP_EOL;
    echo "投递任务的 worker 进程 id:{$src_worker_id}",PHP_EOL;
    echo "任务的数据内容:{$data}",PHP_EOL;
    echo PHP_EOL;
});
// 当工作进程收到由 $server->sendMessage() 发送的 unixSocket 消息时会触发 onPipeMessage 事件
// worker/task 进程都可能会触发 onPipeMessage 事件
$server->on('pipeMessage', function ($server, $src_worker_id, $data) {
    echo 'onPipeMessage->当工作进程收到由 $server->sendMessage() 发送的消息',PHP_EOL;
    // 当前进程是否是 Task 进程
    if ($server->taskworker == true) {
        echo "当前进程类型:当前是task进程", PHP_EOL;
    } else {
        echo "当前进程类型:当前是worker进程", PHP_EOL;
    }
    echo "消息来自的worker或者task进程ID:{$src_worker_id}",PHP_EOL;
    echo "当前处理的worker或者task进程ID:{$server->worker_id}",PHP_EOL;
    echo '消息是:', $data, PHP_EOL;
    echo PHP_EOL;
});
$server->start();

进程

tcp服务启动后,首先会有2个基本的进程:主进程和管理进程,是默认都有的
然后还会有woker进程和task进程,这个需要看下配置:

  1. worker_num进程数量:相当于食堂打饭的时候的窗口,如果窗口比较多,就可以同时服务多个人,当然这个进程的数量不是越多越好,要取决于cpu的核数。因为tcp接收事件onReceive是在worker进程中进行的。
  2. task_worker_num进程数量:属于后台进程,相当于食堂的后勤人员,当你点的菜制作起来比较耗时间,可以交给后勤人员慢慢做,就不会占用着窗口。当然任务有没完成,我们可以通过websocket或者轮询的方式跟前台页面进行交互。使用场景有,给用户发短信,导出数据啥的都可以用这个task进程来做。

进程通信

可以通过sendMessage向woker或者task进程进行发送消息,然后worker进程或者task进程里的onPipeMessage事件里就可以收到消息,该事件存在于worker或者task进程里。
可以通过task方法,向task进程发送消息,然后在task进程的onTask事件里就可以收到消息,该事件只会在task进程里。

0

评论 (0)

取消