您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何通過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)用等能力。
我們以 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)容:
前三個(gè)時(shí)間分別是 mysql
、redis1
、redis2
三個(gè)客戶端發(fā)起請(qǐng)求的時(shí)間,可以看到,盡管 mysql
中會(huì)休眠 3 秒,但是通過 defer 機(jī)制實(shí)現(xiàn)了三個(gè)請(qǐng)求的并發(fā)執(zhí)行。
除了 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é)果如下:
由于 MySQL 請(qǐng)求執(zhí)行耗時(shí)最長(zhǎng),所以位置最靠后。在啟動(dòng)服務(wù)器的終端,可以看到打印出的三個(gè)客戶端請(qǐng)求時(shí)間,完全一致,說明它們是并發(fā)執(zhí)行的:
顯然,通過子協(xié)程 + 通道還可以很方便的實(shí)現(xiàn) Redis、MySQL 連接池。
關(guān)于如何通過swoole協(xié)程實(shí)現(xiàn)并發(fā)編程就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(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)容。