使用 swoole 加速 Laravel5.6 Restful API 介面

Groot發表於2018-12-17
  1. 建立自定義artisan命令
    php artisan make:command SwooleServer //預設在app/Console/Commans目錄下建立SwooleServer.php檔案。可借鑑我的上一遍文章Laravel 5.6 中優雅的管理 swoole 程式程式碼如下:

        <?php
    
        namespace App\Console\Commands;
        use Illuminate\Console\Command;
        use Illuminate\Support\Facades\App;
    
        class SwooleServer extends Command
        {
            //swoole http server instance
            private $server;
    
            //程式ID
            private $pid_file;
    
            //日誌檔案
            private $log_file;
    
            /**
             * The name and signature of the console command.
             *
             * @var string
             */
            protected $signature = 'swoole {action}';
    
            /**
             * The console command description.
             *
             * @var string
             */
            protected $description = 'start or stop the swoole http server';
    
            /**
             * Create a new command instance.
             *
             * @return void
             */
            public function __construct()
            {
                parent::__construct();
                //指定主程式PID儲存檔案
                $this->pid_file = __DIR__ . '/../../../storage/swoole_server.pid';
                //日誌檔案儲存
                $this->log_file = __DIR__ . '/../../../storage/logs/' . date('Ymd') . '_swoole_server.log';
                if (!is_file($this->log_file)) {
                    $resource = fopen($this->log_file, "w");
                    fclose($resource);
                }
            }
    
            /**
             * Execute the console command.
             *
             * @return mixed
             */
            public function handle()
            {
                //獲取傳遞的操作
                $arg = $this->argument('action');
    
                switch ($arg) {
                    case 'start':
                        //檢測程式是否已開啟
                        $pid = $this->getPid();
                        if ($pid && \Swoole\Process::kill($pid, 0)) {
                            $this->error("\r\nswoole http server process already exist!\r\n");
                            exit;
                        }
    
                        $this->server = new \Swoole\Http\Server("0.0.0.0", 9501);
                        $this->server->set([
                            'worker_num' => 8,
                            'daemonize' => 1,
                            'max_request' => 1000,
                            'dispatch_mode' => 2,
                            'pid_file' => $this->pid_file,
                            'log_file' => $this->log_file,
                            'log_level' => 3, //日誌等級 notice
                        ]);
    
                        //繫結操作類回撥函式
                        $app = App::make('App\Handles\SwooleServerHandle');
    
                        $this->server->on('workerstart', array($app, 'onWorkerStart'));
                        $this->server->on('request', array($app, 'onRequest'));
    
                        $this->info("\r\nswoole http server process created successful!\r\n");
    
                        $this->server->start();
                        break;
    
                    case 'stop':
                        if (!$pid = $this->getPid()) {
                            $this->error("\r\nswoole http server process not started!\r\n");
                            exit;
                        }
                        if (\Swoole\Process::kill((int)$pid)) {
                            $this->info("\r\nswoole http server process close successful!\r\n");
                            exit;
                        }
                        $this->info("\r\nswoole http server process close failed!\r\n");
                        break;
    
                    default:
                        $this->error("\r\noperation method does not exist!\r\n");
                }
    
            }
    
            //獲取pid
            private function getPid()
            {
                return file_exists($this->pid_file) ? file_get_contents($this->pid_file) : false;
            }
        }
  2. 編寫操作類SwooleServerHandle

     <?php
    /**
     * Created by PhpStorm.
     * User: kite
     */
    
    namespace App\Handles;
    
    class SwooleServerHandle
    {
        //header Server 全域性變數的對映關係
        private static $headerServerMapping = [
            'x-real-ip'       => 'REMOTE_ADDR',
            'x-real-port'     => 'REMOTE_PORT',
            'server-protocol' => 'SERVER_PROTOCOL',
            'server-name'     => 'SERVER_NAME',
            'server-addr'     => 'SERVER_ADDR',
            'server-port'     => 'SERVER_PORT',
            'scheme'          => 'REQUEST_SCHEME',
        ];
    
        public function onWorkerStart($serv,$worker_id)
        {
            require __DIR__.'/../../vendor/autoload.php';
            require_once __DIR__.'/../../bootstrap/app.php';
        }
    
        public function onRequest($request,$response)
        {
            //server資訊
            $_SERVER =[];
            if (isset($request->server)) {
                foreach ($request->server as $k => $v) {
                    $_SERVER[strtoupper($k)] = $v;
                }
            }
    
            //header頭資訊
            if (isset($request->header)) {
                foreach ($request->header as $key => $value) {
                    if (isset(self::$headerServerMapping[$key])) {
                        $_SERVER[self::$headerServerMapping[$key]] = $value;
                    } else {
                        $key = str_replace('-', '_', $key);
                        $_SERVER[strtoupper('http_' . $key)] = $value;
                    }
                }
            }
            //是否開啟https
            if (isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] === 'https') {
                $_SERVER['HTTPS'] = 'on';
            }
            //request uri
            if (strpos($_SERVER['REQUEST_URI'], '?') === false && isset($_SERVER['QUERY_STRING']) && strlen($_SERVER['QUERY_STRING']) > 0
            ) {
                $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
            }
    
            //全域性的
            if (!isset($_SERVER['argv'])) {
                $_SERVER['argv'] = isset($GLOBALS['argv']) ? $GLOBALS['argv'] : [];
                $_SERVER['argc'] = isset($GLOBALS['argc']) ? $GLOBALS['argc'] : 0;
            }
    
            //get資訊
            $_GET = [];
            if (isset($request->get)) {
                foreach ($request->get as $k => $v) {
                    $_GET[$k] = $v;
                }
            }
    
            //post資訊
            $_POST = [];
            if (isset($request->post)) {
                foreach ($request->post as $k => $v) {
                    $_POST[$k] = $v;
                }
            }
    
            //檔案請求
            $_FILES =[];
            if (isset($request->files)) {
                foreach ($request->files as $k => $v) {
                    $_FILES[$k] = $v;
                }
            }
            //cookie
            $_COOKIE=[];
            if (isset($request->cookie)) {
                foreach ($request->cookie as $k => $v) {
                    $_COOKIE[$k] = $v;
                }
            }
    
            ob_start();//啟用快取區
            \Illuminate\Http\Request::enableHttpMethodParameterOverride();
            $kernel = app()->make(\Illuminate\Contracts\Http\Kernel::class);
            $laravelResponse = $kernel->handle(
                //Create an Illuminate request from a Symfony instance.
                $request =  \Illuminate\Http\Request::createFromBase(new \Symfony\Component\HttpFoundation\Request($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER, $request->rawContent()))
            );
    
            $laravelResponse->send();
            $kernel->terminate($request, $laravelResponse);
            $res = ob_get_contents();//獲取快取區的內容
            ob_end_clean();//清除快取區
            //輸出快取區域的內容
            $response->end($res);
        }
    }

3.使用php artisan swoole start即可開啟服務,在nginx配置代理即可加速你的Restful api介面.
4.在centos7 單核 2G記憶體下的測試結果如下:
未使用swoole的吞吐量只有2.44
file
使用了swoole的吞吐量提升到了56.19且加大併發數吞吐量都可以維持一個水平
file

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章