PHP

总结一些实际项目中的优化案例

2018-10-22  本文已影响0人  守望星空l

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% ,效果是非常明显的。

上一篇下一篇

猜你喜欢

热点阅读