中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

PHP中怎么搭建一個(gè)HTTP服務(wù)

本篇文章給大家分享的是有關(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è)Channelsize設(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)

商城網(wǎng)站建設(shè)
论坛| 靖安县| 龙岩市| 玉山县| 家居| 政和县| 神池县| 乐亭县| 铜陵市| 巴林右旗| 安仁县| 达拉特旗| 萝北县| 云龙县| 浦东新区| 固原市| 喀喇| 卓资县| 永宁县| 子洲县| 凤凰县| 车险| 郧西县| 和顺县| 安乡县| 通化市| 株洲县| 乐安县| 宣武区| 云霄县| 东阿县| 特克斯县| 安仁县| 株洲市| 吉木乃县| 武功县| 交口县| 江孜县| 冕宁县| 察隅县| 彩票|