溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程

發(fā)布時(shí)間:2021-11-15 15:39:29 來源:億速云 閱讀:309 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

目前的Swoole 內(nèi)置了豐富的協(xié)程組件供開發(fā)者直接調(diào)用以便快速實(shí)現(xiàn)異步非阻塞的并發(fā)編程,省去了開發(fā)者自己實(shí)現(xiàn)相應(yīng)底層代碼的麻煩:

TCP/UDP Client:Swoole\Coroutine\Client
TCP/UDP Server:Swoole\Coroutine\Server
HTTP/WebSocket Client:Swoole\Coroutine\HTTP\Client
HTTP/WebSocket Server:Swoole\Coroutine\HTTP\Server
HTTP2 Client:Swoole\Coroutine\HTTP2\Client
Redis Client:Swoole\Coroutine\Redis
Mysql Client:Swoole\Coroutine\MySQL
PostgreSQL Client:Swoole\Coroutine\PostgreSQL

在協(xié)程 Server 中使用對(duì)應(yīng)的協(xié)程版 Client 來實(shí)現(xiàn)全異步 Server,同時(shí) Swoole 提供了協(xié)程工具集:Swoole\Coroutine,提供了獲取當(dāng)前協(xié)程ID、反射調(diào)用等能力。

通過 setDefer 機(jī)制實(shí)現(xiàn)并發(fā)編程

我們以 Redis 和 MySQL 客戶端請(qǐng)求為例,使用上述 Swoole\Coroutine\Redis 和 Swoole\Coroutine\MySQL 組件,可以實(shí)現(xiàn)異步 Redis 和 MySQL 客戶端:

<?php$server = new \Swoole\Http\Server('127.0.0.1', 9588);$server->on('Request', function ($request, $response) {var_dump(time());$mysql = new Swoole\Coroutine\MySQL();$mysql->connect(['host' => '127.0.0.1','user' => 'root','password' => 'root','database' => 'laravel58',]);$mysql->setDefer();$mysql->query('select sleep(3)');var_dump(time());$redis1 = new Swoole\Coroutine\Redis();$redis1->connect('127.0.0.1', 6379);$redis1->setDefer();$redis1->set('hello', 'world');var_dump(time());$redis2 = new Swoole\Coroutine\Redis();$redis2->connect('127.0.0.1', 6379);$redis2->setDefer();$redis2->get('hello');$result1 = $mysql->recv();$result2 = $redis2->recv();var_dump($result1, $result2, time());$response->end('Request Finish: ' . time());});$server->start();

由于 Swoole 會(huì)在 TCP Server 和 HTTP Server 回調(diào)函數(shù)中會(huì)自動(dòng)開啟協(xié)程,所以不需要顯式通過 go 關(guān)鍵字啟動(dòng)協(xié)程,然后我們可以在回調(diào)函數(shù)中使用 MySQL 和 Redis 客戶端協(xié)程組件發(fā)起請(qǐng)求。

要理解上述代碼的運(yùn)行原理,需要先了解協(xié)程的 setDefer 機(jī)制,絕大部分協(xié)程組件都支持 setDefer,該機(jī)制可以將請(qǐng)求響應(yīng)式的接口拆分為兩個(gè)步驟:先發(fā)送數(shù)據(jù), 再并發(fā)收取響應(yīng)結(jié)果。

由于大多數(shù)情況下,「建立連接和發(fā)送數(shù)據(jù)的耗時(shí)」相較于「等待響應(yīng)的耗時(shí)」來說可以忽略不計(jì), 所以可以簡(jiǎn)單理解為 defer 模式下, 多個(gè)客戶端的請(qǐng)求響應(yīng)是并發(fā)的(實(shí)際上只有接收響應(yīng)是并發(fā)的,建立連接和發(fā)送請(qǐng)求是串行的)。

以上述代碼為例,設(shè)置 setDefer(true) 后,通過 Redis 或 MySQL 客戶端發(fā)起請(qǐng)求,將不再等待服務(wù)器返回結(jié)果,而是在發(fā)送請(qǐng)求之后,立即返回 true。在此之后可以繼續(xù)發(fā)起其他 Redis、MySQL 請(qǐng)求,最后再使用 recv() 方法接收響應(yīng)內(nèi)容。

我們將上述代碼保存到 coroutine/http.php,然后在終端啟動(dòng)這個(gè) HTTP 服務(wù)端:

php coroutine/http.php

接下來,在 Postman 中對(duì)服務(wù)端發(fā)起請(qǐng)求,會(huì)在等待幾秒后看到返回的響應(yīng)內(nèi)容:

如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程

前三個(gè)時(shí)間分別是 mysql、redis1、redis2 三個(gè)客戶端發(fā)起請(qǐng)求的時(shí)間,可以看到,盡管 mysql 中會(huì)休眠 3 秒,但是通過 defer 機(jī)制實(shí)現(xiàn)了三個(gè)請(qǐng)求的并發(fā)執(zhí)行。

通過子協(xié)程+通道實(shí)現(xiàn)并發(fā)編程

除了 setDefer 機(jī)制外,Swoole 還支持通過子協(xié)程+通道實(shí)現(xiàn)并發(fā)編程,下面我們通過子協(xié)程+通道的方式來改寫上面的代碼實(shí)現(xiàn):

<?php$server = new \Swoole\Http\Server('127.0.0.1', 9588);$server->on('Request', function ($request, $response) {$channel = new \Swoole\Coroutine\Channel(3);go(function () use ($channel) {var_dump(time());$mysql = new Swoole\Coroutine\MySQL();$mysql->connect(['host' => '127.0.0.1','user' => 'root','password' => 'root','database' => 'laravel58',]);$result = $mysql->query('select sleep(3)');$channel->push($result);});go(function () use ($channel) {var_dump(time());$redis1 = new Swoole\Coroutine\Redis();$redis1->connect('127.0.0.1', 6379);$result = $redis1->set('hello', 'world');$channel->push($result);});go(function () use ($channel) {var_dump(time());$redis2 = new Swoole\Coroutine\Redis();$redis2->connect('127.0.0.1', 6379);$result = $redis2->get('hello');$channel->push($result);});$results = [];for ($i = 0; $i < 3; $i++) {$results[] = $channel->pop();}$response->end(json_encode(['data' => $results,'time' => time()]));});$server->start();

我們將 MySQL 和 Redis 客戶端連接請(qǐng)求調(diào)用改寫為通過三個(gè)子協(xié)程實(shí)現(xiàn),同時(shí)去掉 setDefer 設(shè)置,因?yàn)檫@三個(gè)子協(xié)程已經(jīng)是并發(fā)調(diào)用了,此外,由于三個(gè)子協(xié)程之間數(shù)據(jù)是相互隔離的,所以我們通過 Swoole\Coroutine\Channel (即通道)實(shí)現(xiàn)協(xié)程之間的數(shù)據(jù)共享和通信,初始化其緩沖空間為 3,然后通過 use 方式將其引入到子協(xié)程中,把響應(yīng)結(jié)果通過 push 方法放到 Channel 里面,接下來在服務(wù)端 onRequest 回調(diào)函數(shù)末尾通過一個(gè)循環(huán)將 Channel 中的數(shù)據(jù)通過 pop 方法依次取出來放到數(shù)組 $results 中,最后通過 $response->end() 方法將結(jié)果以 JSON 格式返回給客戶端。

我們將上述代碼保存到 coroutine/http2.php,然后在終端通過如下命令啟動(dòng)這個(gè)新的 HTTP 服務(wù)端:

php coroutine/http2.php

還是在 Postman 中請(qǐng)求這個(gè)服務(wù)端,將響應(yīng)格式調(diào)整為 JSON,會(huì)看到結(jié)果如下:

如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程

由于 MySQL 請(qǐng)求執(zhí)行耗時(shí)最長(zhǎng),所以位置最靠后。在啟動(dòng)服務(wù)器的終端,可以看到打印出的三個(gè)客戶端請(qǐng)求時(shí)間,完全一致,說明它們是并發(fā)執(zhí)行的:

如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程

顯然,通過子協(xié)程 + 通道還可以很方便的實(shí)現(xiàn) Redis、MySQL 連接池。

關(guān)于如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI