Swoole4.x探究之多进程TCP协程服务实现

有研究过Workman框架的同窗就会发现,其实workman最核心的,就是用了php socket拓展加上pcntl拓展来实现其底层的网络服务和多进程调度。那咱们今天就来探讨如何使用Swoole的CoroutineSocket模块来实现本身的tcp服务。
咱们先编写一段小的测试代码,test.php 代码以下php

$socket = new Co\Socket(AF_INET, SOCK_STREAM, 0);
$socket->bind('127.0.0.1', 9601);
$socket->listen(128);

go(function () use ($socket) {
    while(true) {
        $client = $socket->accept(-1);
        $data = $client->recv(64,10);
        var_dump('Recv:'.$data);
        $client->sendAll('reply at '.time());
        $client->close();
    }
});

咱们执行git

php test.php

并新建一个cmd控制台,用telnet模拟tcp客户端,能够看到以下结果:github

telnet 127.0.0.1 9601
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
asd
reply at 1559713416
Connection closed by foreign host

以上说明咱们已经成功创建了一个简单的TCP服务器。而有细心的同窗就会发现,以上代码若是我在recv后,有一些数据库行为发生,那我该TCP服务器在同一时间就仅仅只能accept连接并处理一个连接的事情,并发能力接近于1。所以咱们能够作一点小小的改进,以下:数据库

$socket = new Co\Socket(AF_INET, SOCK_STREAM, 0);
$socket->bind('127.0.0.1', 9601);
$socket->listen(128);

go(function () use ($socket) {
    while(true) {
        $client = $socket->accept(-1);
        go(function () use ($client){
            $data = $client->recv(64,10);
            var_dump('Recv:'.$data);
            //模拟数据库耗时,假定咱们的数据库也是用协程api
            \co::sleep(1);
            $client->sendAll('reply at '.time());
            $client->close();
        });
    }
});

咱们利用协程,把链接accept后的逻辑,所有放到另一个子协程当中处理,让咱们的TCP服务器能够继续accept链接,也就提升了咱们的并发能力。然而,在实际的编程中,咱们不可能作到彻底的百分百协程API,并且个人机器也是多核心的处理器,那么此刻我如何尽量的利用个人CPU呢?所以咱们能够利用端口复用的特性和Swoole的Process来构建一个多进程TCP协程服务器。编程

引入Process

由于在本章节中,对Swoole Process的封装不是咱们关心的,所以咱们这里直接使用EasySwoole封装好的进程组件。api

composer require easyswoole/component

实现个人Process Class,代码以下服务器

use Co\Socket;
use EasySwoole\Component\Process\AbstractProcess;

class Server extends AbstractProcess
{
    protected function run($arg)
    {
        $socket = new Socket(AF_INET, SOCK_STREAM, 0);
        //关键在这里,容许复用
        $socket->setOption(SOL_SOCKET,SO_REUSEPORT,true);
        $socket->setOption(SOL_SOCKET,SO_REUSEADDR,true);
        $socket->bind('127.0.0.1', 9601);
        $socket->listen(128);
        go(function () use ($socket) {
            while(true) {
                $client = $socket->accept(-1);
                go(function () use ($client){
                    $data = $client->recv(64,10);
                    var_dump('Recv:'.$data);
                    //模拟数据库耗时,假定咱们的数据库也是用协程api
                    \co::sleep(1);
                    $client->sendAll('reply at '.time());
                    $client->close();
                });
            }
        });
    }
}

咱们在以上代码中,加入了容许端口复用的选项,不然在多进程模式下,会致使监听失败。咱们在cli模式下,测试起五个进程swoole

for ($i = 1;$i < 5;$i++){
    $p = new Server("TcpServer.{$i}");
    $p->getProcess()->start();
}
//主进程要等待回收
while($ret = \Swoole\Process::wait()) {
    echo "PID={$ret['pid']}\n";
}

咱们,这样就很简单的实现了一个多进程的TCP协程服务。总而言之,Swoole 4.x的协程能力,仍是很强大的,让PHPer能够以最小的代价来实现一个高性能的TCP服务,而不是须要去学习新的一门语言,若须要更多的完善代码,能够参考http://easyswoole.com/ 这个框架的项目代码,若是有喜欢的同窗,能够随意点个 star ,github地址 https://github.com/easy-swool...网络

相关文章
相关标签/搜索