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...網絡

相關文章
相關標籤/搜索