SWOOLE
之前的項目里有2個php寫的統計接口,一天的訪問量在3500W左右,
項目的總接口訪問量在7000W多點。
在這種情況下,我嘗試用swoole重寫了這2個統計接口,效果非常明顯。
優化前是12臺4核服務器 CPU占用40%, 優化后12臺服務器的CPU占用減少了一半。
部分代碼如下;
<?php
/**
* Created by PhpStorm.
* User: xueyb
* Date: 2018/08/01
* Time: 下午14:52
*/
date_default_timezone_set('PRC');
define("ROOT_PATH", __DIR__ . '/../..');
use Swoole\Table;
class CooStat
{
public $server;
const TASK_TYPE_VC_ONLINE = 1;
private $redisConfig = [];
private $memConfig = [];
public function run()
{
ini_set('display_errors', 1);
error_reporting(E_ALL);
$this->redisConfig = include(ROOT_PATH . '/config/redis.php');
$this->memConfig = include(ROOT_PATH . '/config/memcache.php');
$serverConfig = include(ROOT_PATH . '/config/cooStatSetting.php');
$serverConfig['log_file'] = $serverConfig['log_file'] ?: ROOT_PATH . '/logs/swoole.log';
$config = include(ROOT_PATH . '/config/config.php');
$this->server = new swoole_http_server("0.0.0.0", 9501);
$this->server->set($serverConfig);
// $this->server->on('connect', [$this, 'onConnect']);
$this->server->on('request', [$this, 'onRequest']);
// $this->server->on('close', [$this, 'onClose']);
$this->server->on('WorkerStart', [$this, 'onWorkerStart']);
$this->server->on('task', [$this, 'onTask']);
$this->server->on('finish', [$this, 'onFinish']);
//創建內存表 存儲驗證失敗的
$checkFailTable = new Table(65536);
$checkFailTable->column('netbarId', Table::TYPE_INT, 4);
$checkFailTable->column('tokenMd5', Table::TYPE_STRING, 32);
$checkFailTable->column('updateTime', Table::TYPE_INT, 4);
$checkFailTable->create();
$this->server->checkFailTable = $checkFailTable;
$this->server->otherConfig = $config;
$this->server->start();
}
public function onWorkerStart($serv, $worker_id)
{
include_once(ROOT_PATH . '/src/function/CooStat.php');
include_once(ROOT_PATH . '/src/function/Comm.php');
if ($worker_id >= 4) {
$serv->redisOnline = redisConn($this->redisConfig['online']);
$serv->redisTerminalList = redisConn($this->redisConfig['terminal_list']);
$serv->redisTerminalInfo = redisConn($this->redisConfig['terminal_info']);
$serv->memPrime = memcacheConn($this->memConfig['prime']);
// $serv->tick(3000, [$this, 'connNumTimer'], ['serv' => $serv]);
}
}
public function onRequest($request, $response)
{
$method = $request->server['request_method'];
$pathInfo = $request->server['path_info'];
$routerPath = strtolower($method) . ':' . strtolower($pathInfo);
switch ($routerPath) {
case 'post:/vc/online':
$res = vcOnlineDealRequest($this->server, $request);
break;
case 'get:/swoole_server_status':
$res = 'server status: ' . json_encode($this->server->stats()) . ' swoole_table_count : ' . $this->server->checkFailTable->count();
break;
case 'post:/clientlistvc/info':
$res = vcClientDealRequest($this->server, $request);
break;
default:
$res = 'no router match';
}
$response->end($res);
/* 協程測試
go(function () use ($serv, $fd, $data) {
$http = new Co\Http\Client("www.baidu.com", 443, true);
$http->setHeaders([]);
$http->set(['timeout' => 3]);
$ret = $http->get('/');
if ($http->statusCode < 0) {
echo $http->errCode . PHP_EOL;
}
});
*/
}
public function onTask($serv, int $task_id, int $src_worker_id, array $params = [])
{
$type = $params['type'];
switch ($type) {
case 'vcOnline':
$res = vcOnlineTask($serv, $task_id, $src_worker_id, $params);
break;
case 'vcClient':
$res = vcClientTask($serv, $task_id, $src_worker_id, $params);
break;
default :
$res = 'no type match';
}
return $res;
}
public function onFinish(swoole_server $serv, int $task_id, string $data)
{
}
public function onClose($serv, $fd)
{
}
}
$server = new CooStat();
$server->run();
重寫第一個接口時負載變化:
Redis
有時候我們需要連續多次操作redis 如:
<?php
for($i=0; $i<100; $i++) {
//偽代碼 舉個例子
Redis::set(...)
}
如果訪問量小,怎么寫都無所謂,但如果是流量比較大的項目,這樣的代碼肯定是有性能問題的。
redis pipeline (ps:主要是總結實踐過的案例,不對原理做過多的介紹...) 用在這樣的場景下就非常合適,經過實測,性能比普通方式提升4-5倍 不僅明顯提升php的執行速度 還能大幅降低redis服務器的壓力。
以下是實際項目使用pipeline前后的性能對比:
可以看到 一臺 每秒執行30000次左右命令的redis server 在使用了pipeline 之后,cpu 由80% 降低到 30% ,效果是非常明顯的。