使用 swoole 加速 Laravel5.6 Restful API 接口

  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 协议》,转载必须注明作者和本文链接
死磕渴望的知识,不要放弃,终将会有所收获。
本帖由系统于 2年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 14

每次修改代码貌似要重启进程

3年前 评论

@lovecn 是的,就这两个文件写的比较简单

3年前 评论

使用 swoole 加速 lumen 不成功 。。

3年前 评论

@Cium_ 这个没在lumen上测试过,我在同一个虚拟中的测试结果可有提升25倍左右吞吐

3年前 评论

可以写个 支持 lumen 的吗 ,使用lumen 比较多。

3年前 评论

@Cium_ 其实都是一样的原理,你需要把框架的响应写在onRequest方法内即可,我去看下lumen

3年前 评论

加速API,按照这个方法,我在本地虚拟机上测试,同样是单核2G,优势体现不出来,请求报错数反而增加,什么情况?

1年前 评论

@gxcnvip app\Http\Kernel.php这个文件中把中间件移除试下

file

1年前 评论

使用laravel-swoole包,http请求中,如何主动发送websocket?

1年前 评论

@jeffrey_zou websockect要你客户端实现啊

1年前 评论

@Groot 客户端已经连上websocket了, 但是服务端主动发websocket消息,就报错,是跨进程了? 怎么解决?

1年前 评论

@jeffrey_zou 贴代码啊,这样没法接啊 :flushed:

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!