本篇文章給大家分享的是有關(guān)PHP中怎么搭建一個(gè)HTTP服務(wù),小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。
成都創(chuàng)新互聯(lián)成立于2013年,先為嘉陵等服務(wù)建站,嘉陵等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為嘉陵企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。
<?php use Swoole\Http\Request; use Swoole\Http\Response; $process = new Swoole\Process(function (Swoole\Process $process) { $server = new Swoole\Http\Server('127.0.0.1', 9501, SWOOLE_BASE); $server->set([ 'log_file' => '/dev/null', 'log_level' => SWOOLE_LOG_INFO, 'worker_num' => swoole_cpu_num() * 2, // 'hook_flags' => SWOOLE_HOOK_ALL, ]); $server->on('workerStart', function () use ($process, $server) { $process->write('1'); }); $server->on('request', function (Request $request, Response $response) use ($server) { try { $redis = new Redis; $redis->connect('127.0.0.1', 6379); $greeter = $redis->get('greeter'); if (!$greeter) { throw new RedisException('get data failed'); } $response->end("<h2>{$greeter}</h2>"); } catch (\Throwable $th) { $response->status(500); $response->end(); } }); $server->start(); }); if ($process->start()) { register_shutdown_function(function () use ($process) { $process::kill($process->pid); $process::wait(); }); $process->read(1); System('ab -c 256 -n 10000 -k http://127.0.0.1:9501/ 2>&1'); }
首先,我們創(chuàng)建了一個(gè)Swoole\Process
對(duì)象,這個(gè)對(duì)象會(huì)開(kāi)啟一個(gè)子進(jìn)程,在子進(jìn)程中,我創(chuàng)建了一個(gè)HTTP Server
,這個(gè)服務(wù)器是BASE
模式的。除了BASE
模式之外,還有一種PROCESS
模式。在PROCESS
模式下,套接字連接是在Master
進(jìn)程維持的,Master
進(jìn)程和Worker
進(jìn)程會(huì)多一層IPC
通信的開(kāi)銷,但是,當(dāng)Worker
進(jìn)程奔潰的時(shí)候,因?yàn)檫B接是在Master
進(jìn)程維持的,所以連接不會(huì)被斷開(kāi)。所以,Process
模式適用于維護(hù)大量長(zhǎng)連接的場(chǎng)景。
BASE
模式是在每個(gè)工作進(jìn)程維持自己的連接,所以性能會(huì)比Master
更好。并且,在HTTP Server
下,BASE
模式會(huì)更加的適用。
這里,我們將worker_num
,也就是進(jìn)程的數(shù)量設(shè)置為當(dāng)前機(jī)器CPU
核數(shù)的兩倍。但是,在實(shí)際的項(xiàng)目中,我們需要不斷的壓測(cè),來(lái)調(diào)整這個(gè)參數(shù)。
在workerStart
的時(shí)候,也就是工作進(jìn)程啟動(dòng)的時(shí)候,我們讓子進(jìn)程向管道中隨意寫(xiě)入一個(gè)數(shù)據(jù)給父進(jìn)程,父進(jìn)程此時(shí)會(huì)讀到一點(diǎn)數(shù)據(jù),讀到數(shù)據(jù)后,父進(jìn)程才開(kāi)始?jí)簻y(cè)。
此時(shí),壓測(cè)的請(qǐng)求會(huì)進(jìn)入onRequest
回調(diào)。在這個(gè)回調(diào)中,我們創(chuàng)建了一個(gè)Redis
客戶端,這個(gè)客戶端會(huì)連接Redis
服務(wù)器,并請(qǐng)求一條數(shù)據(jù)。得到數(shù)據(jù)后,我們調(diào)用end
方法來(lái)響應(yīng)壓測(cè)的請(qǐng)求。當(dāng)錯(cuò)誤時(shí),我們返回一個(gè)錯(cuò)誤碼為500
的響應(yīng)。
在開(kāi)始?jí)簻y(cè)前,我們需要安裝Redis
擴(kuò)展:
pecl install redis
然后php.ini
配置中開(kāi)啟redis
擴(kuò)展即可。
我們還需要在Redis
服務(wù)器里面插入一條數(shù)據(jù):
127.0.0.1:6379> SET greeter swoole OK 127.0.0.1:6379> GET greeter "swoole" 127.0.0.1:6379>
OK
,我們現(xiàn)在進(jìn)行壓測(cè):
~/codeDir/phpCode/swoole/server # php server.php Concurrency Level: 256 Time taken for tests: 2.293 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 1680000 bytes HTML transferred: 150000 bytes Requests per second: 4361.00 [#/sec] (mean) Time per request: 58.702 [ms] (mean) Time per request: 0.229 [ms] (mean, across all concurrent requests) Transfer rate: 715.48 [Kbytes/sec] received
我們發(fā)現(xiàn),現(xiàn)在的QPS
比較低,只有4361.00
。
因?yàn)?,我們目前使用?code>Redis擴(kuò)展是PHP
官方的同步阻塞客戶端,沒(méi)有利用到協(xié)程(或者說(shuō)異步的特性)。當(dāng)進(jìn)程去連接Redis
服務(wù)器的時(shí)候,可能會(huì)阻塞整個(gè)進(jìn)程,導(dǎo)致進(jìn)程無(wú)法處理其他的連接,這樣,這個(gè)HTTP Server
處理請(qǐng)求的速度就不可能快。但是,這個(gè)壓測(cè)結(jié)果會(huì)比FPM
下好,因?yàn)?code>Swoole是常駐進(jìn)程的。
現(xiàn)在,我們來(lái)開(kāi)啟Swoole
提供的RuntimeHook
機(jī)制,也就是在運(yùn)行時(shí)動(dòng)態(tài)的將PHP
同步阻塞的方法全部替換為異步非阻塞的協(xié)程調(diào)度的方法。我們只需要在server->set
配置中加入一行即可:
'hook_flags' => SWOOLE_HOOK_ALL
此時(shí),我們?cè)賮?lái)運(yùn)行這個(gè)腳本:
Concurrency Level: 256 Time taken for tests: 1.643 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 1680000 bytes HTML transferred: 150000 bytes Requests per second: 6086.22 [#/sec] (mean) Time per request: 42.062 [ms] (mean) Time per request: 0.164 [ms] (mean, across all concurrent requests) Transfer rate: 998.52 [Kbytes/sec] received
我們發(fā)現(xiàn),此時(shí)的QPS
還是有一定的提升的。(這里,視頻中壓測(cè)的時(shí)候,會(huì)夯住請(qǐng)求,導(dǎo)致QPS
非常的低,但是我實(shí)際測(cè)試的時(shí)候沒(méi)有發(fā)生這個(gè)情況,估計(jì)是和Redis
服務(wù)器本身對(duì)連接個(gè)數(shù)的的配置有關(guān))
但是,為了避免請(qǐng)求數(shù)量過(guò)多,導(dǎo)致創(chuàng)建連接個(gè)數(shù)過(guò)多的問(wèn)題,我們可以使用一個(gè)Redis
連接池來(lái)解決。(同步阻塞是沒(méi)有Redis
連接過(guò)多的問(wèn)題的,因?yàn)橐坏?code>worker進(jìn)程阻塞住了,那么后面的請(qǐng)求就不會(huì)繼續(xù)執(zhí)行了,也就不會(huì)創(chuàng)建新的Redis
連接了。因此,在同步阻塞的模式下,Redis
的連接數(shù)量最大是worker
進(jìn)程的個(gè)數(shù))
現(xiàn)在,我們來(lái)實(shí)現(xiàn)一下Redis
連接池:
class RedisQueue { protected $pool; public function __construct() { $this->pool = new SplQueue; } public function get(): Redis { if ($this->pool->isEmpty()) { $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); return $redis; } return $this->pool->dequeue(); } public function put(Redis $redis) { $this->pool->enqueue($redis); } public function close() { $this->pool = null; } }
這里通過(guò)spl
的隊(duì)列實(shí)現(xiàn)的連接池。如果連接池中沒(méi)有連接的時(shí)候,我們就新建一個(gè)連接,并且把創(chuàng)建的這個(gè)連接返回;如果連接池里面有連接,那么我們獲取隊(duì)列中前面的一個(gè)連接。當(dāng)我們用完連接的時(shí)候,就可以調(diào)用put
方法歸還連接。這樣,我們就可以在一定程度上復(fù)用Redis
的連接,緩解Redis
服務(wù)器的壓力,以及頻繁創(chuàng)建Redis
連接的開(kāi)銷也會(huì)降低。
我們現(xiàn)在使用這個(gè)連接池隊(duì)列:
<?php use Swoole\Http\Request; use Swoole\Http\Response; $process = new Swoole\Process(function (Swoole\Process $process) { $server = new Swoole\Http\Server('127.0.0.1', 9501, SWOOLE_BASE); $server->set([ 'log_file' => '/dev/null', 'log_level' => SWOOLE_LOG_INFO, 'worker_num' => swoole_cpu_num() * 2, 'hook_flags' => SWOOLE_HOOK_ALL, ]); $server->on('workerStart', function () use ($process, $server) { $server->pool = new RedisQueue; $process->write('1'); }); $server->on('request', function (Request $request, Response $response) use ($server) { try { $redis = $server->pool->get(); // $redis = new Redis; // $redis->connect('127.0.0.1', 6379); $greeter = $redis->get('greeter'); if (!$greeter) { throw new RedisException('get data failed'); } $server->pool->put($redis); $response->end("<h2>{$greeter}</h2>"); } catch (\Throwable $th) { $response->status(500); $response->end(); } }); $server->start(); }); if ($process->start()) { register_shutdown_function(function () use ($process) { $process::kill($process->pid); $process::wait(); }); $process->read(1); System('ab -c 256 -n 10000 -k http://127.0.0.1:9501/ 2>&1'); } class RedisQueue { protected $pool; public function __construct() { $this->pool = new SplQueue; } public function get(): Redis { if ($this->pool->isEmpty()) { $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); return $redis; } return $this->pool->dequeue(); } public function put(Redis $redis) { $this->pool->enqueue($redis); } public function close() { $this->pool = null; } }
我們?cè)?code>worker進(jìn)程初始化的時(shí)候,創(chuàng)建了這個(gè)RedisQueue
。然后在onRequest
的階段,從這個(gè)RedisQueue
里面獲取一個(gè)Redis
連接。
現(xiàn)在,我們來(lái)進(jìn)行壓測(cè):
Concurrency Level: 256 Time taken for tests: 1.188 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 1680000 bytes HTML transferred: 150000 bytes Requests per second: 8416.18 [#/sec] (mean) Time per request: 30.418 [ms] (mean) Time per request: 0.119 [ms] (mean, across all concurrent requests) Transfer rate: 1380.78 [Kbytes/sec] received
QPS
提升到了8416.18
。
但是,通過(guò)splqueue
實(shí)現(xiàn)的連接池是有缺陷的,因?yàn)檫@個(gè)隊(duì)列是可以無(wú)限長(zhǎng)的。這樣,當(dāng)并發(fā)量特別大的時(shí)候,還是會(huì)有可能創(chuàng)建非常多的連接,因?yàn)檫B接池里面可能始終都是空的。
這個(gè)時(shí)候,我們可以使用Channel
來(lái)實(shí)現(xiàn)連接池。代碼如下:
class RedisPool { protected $pool; public function __construct(int $size = 100) { $this->pool = new \Swoole\Coroutine\Channel($size); for ($i = 0; $i < $size; $i++) { while (true) { try { $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); $this->put($redis); break; } catch (\Throwable $th) { usleep(1 * 1000); continue; } } } } public function get(): \Redis { return $this->pool->pop(); } public function put(\Redis $redis) { $this->pool->push($redis); } public function close() { $this->pool->close(); $this->pool = null; } }
可以看到,我們?cè)谶@個(gè)構(gòu)造方法中,將這個(gè)Channel
的size
設(shè)置為這個(gè)傳入的參數(shù)。并且,創(chuàng)建size
個(gè)連接。這些連接會(huì)在初始化連接池的時(shí)候就被創(chuàng)建,處于就就緒狀態(tài)。這個(gè)有好處也有壞處,壞處就是在每個(gè)進(jìn)程初始化的時(shí)候,就會(huì)占用一些連接,但是此時(shí)的進(jìn)程并不會(huì)接收連接。好處就是提前創(chuàng)建好了Redis
連接,這樣服務(wù)器響應(yīng)的延遲就會(huì)降低。
雖然,其他地方的代碼其實(shí)和RedisQueue
的實(shí)現(xiàn)一樣。但是,底層是和RedisQueue
大有不同的。因?yàn)楫?dāng)Channel
里面沒(méi)有Redis
連接的時(shí)候,會(huì)讓當(dāng)前的協(xié)程掛起,讓其他的協(xié)程繼續(xù)被執(zhí)行。等有協(xié)程把Redis
連接還回到連接池里面的時(shí)候,這個(gè)被掛起的協(xié)程才會(huì)繼續(xù)執(zhí)行。這就是協(xié)程協(xié)作的原理。
現(xiàn)在,我們修改服務(wù)器的代碼:
<?php use Swoole\Http\Request; use Swoole\Http\Response; $process = new Swoole\Process(function (Swoole\Process $process) { $server = new Swoole\Http\Server('127.0.0.1', 9501, SWOOLE_BASE); $server->set([ 'log_file' => '/dev/null', 'log_level' => SWOOLE_LOG_INFO, 'worker_num' => swoole_cpu_num() * 2, 'hook_flags' => SWOOLE_HOOK_ALL, ]); $server->on('workerStart', function () use ($process, $server) { $server->pool = new RedisPool(64); $process->write('1'); }); $server->on('request', function (Request $request, Response $response) use ($server) { try { $redis = $server->pool->get(); // $redis = new Redis; // $redis->connect('127.0.0.1', 6379); $greeter = $redis->get('greeter'); if (!$greeter) { throw new RedisException('get data failed'); } $server->pool->put($redis); $response->end("<h2>{$greeter}</h2>"); } catch (\Throwable $th) { $response->status(500); $response->end(); } }); $server->start(); }); if ($process->start()) { register_shutdown_function(function () use ($process) { $process::kill($process->pid); $process::wait(); }); $process->read(1); System('ab -c 256 -n 10000 -k http://127.0.0.1:9501/ 2>&1'); } class RedisPool { protected $pool; public function __construct(int $size = 100) { $this->pool = new \Swoole\Coroutine\Channel($size); for ($i = 0; $i < $size; $i++) { while (true) { try { $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); $this->put($redis); break; } catch (\Throwable $th) { usleep(1 * 1000); continue; } } } } public function get(): \Redis { return $this->pool->pop(); } public function put(\Redis $redis) { $this->pool->push($redis); } public function close() { $this->pool->close(); $this->pool = null; } }
只需要修改workerStart
里面的部分即可,其他地方不需要做修改。這樣,每個(gè)進(jìn)程最多只能創(chuàng)建64
個(gè)Redis
連接。
我們繼續(xù)壓測(cè):
Concurrency Level: 256 Time taken for tests: 0.817 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 1680000 bytes HTML transferred: 150000 bytes Requests per second: 12234.30 [#/sec] (mean) Time per request: 20.925 [ms] (mean) Time per request: 0.082 [ms] (mean, across all concurrent requests) Transfer rate: 2007.19 [Kbytes/sec] received
以上就是PHP中怎么搭建一個(gè)HTTP服務(wù),小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前文章:PHP中怎么搭建一個(gè)HTTP服務(wù)
轉(zhuǎn)載來(lái)源:http://www.rwnh.cn/article22/jipdcc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、全網(wǎng)營(yíng)銷推廣、定制開(kāi)發(fā)、關(guān)鍵詞優(yōu)化、面包屑導(dǎo)航、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)