在 laradock 环境中使用 laravel-swoole 加速你的 laravel 应用
发表于|更新于|码不能停
|总字数:246|阅读时长:1分钟|浏览量:
在 laradock 环境中使用 laravel-swoole 加速你的 laravel 应用。
安装laravel-swoole
1 | composer require swooletw/laravel-swoole |
开放 workspace 端口
在 laradock/workspace/Dockerfile 最后添加一行:
1 | EXPOSE 1215 |
然后重新 build workspace 容器。
修改 nginx 配置
1 | upstream swoole-http { |
然后重启 nginx
修改 laravel env
1 | SWOOLE_HTTP_HOST=workspace |
启动 swoole
1 | php artisan swoole:http start | stop | restart | resload |
开发环境热更新
调整 swoole_http 中 max_request = 1
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

2019-10-12
在 laradock 环境中使用 laravel-swoole 的 websocket
之前写了一篇 在 laradock 环境中使用 laravel-swoole 加速你的 laravel 应用 的博客,算是敲开了 laravel-swoole 的大门,今天就继续研究下期中的 websocket 功能。 安装 laravel-swoole这里就不写了,参考上篇。 配置 socket编辑 laravel env 文件,写入如下一行: 1SWOOLE_HTTP_WEBSOCKET=true 然后执行 php artisan swoole:http infos 查看状态 1234567891011121314151617+-----------------+-----------------------------------------------------+| Name | Value |+-----------------+-----------------------------------------------------+| PHP Version | 7.2.21-1+ubuntu16.04.1+deb.sury.org+1 || Swoole Version | 4.4.4 || Laravel Version | 5.8.33 || Listen IP | workspace || Listen Port | 1215 || Server Status | Online || Reactor Num | 2 || Worker Num | 2 || Task Worker Num | 2 || Websocket Mode | On || Master PID | 326 || Manager PID | 327 || Log Path | /var/www/laravel-learn/storage/logs/swoole_http.log |+-----------------+-----------------------------------------------------+ 可以看到 Websocket Mode 一项已经打开了。 编辑 socket 路由在 routes/websocket.php 中: 1234567891011121314Websocket::on('connect', function ($websocket, Request $request) { // called while socket on connect $websocket->emit('message', 'welcome');});Websocket::on('disconnect', function ($websocket) { // called while socket on disconnect $websocket->emit('message', 'server is gone, bye~');});Websocket::on('example', function ($websocket, $data) { $websocket->emit('message', $data);}); 此文件修改后需要重启 swoole:http。 前端连接 socket因为 laravel-swoole 使用的是 Socket.io,所以这里也采用这个。 12345678910111213141516<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script><script type="text/javascript"> var socket = io('http://study.test:8080/', {transports: ['websocket'], reconnection: true}); socket.on('connect', function () { socket.emit('example', 'hi'); socket.on('disconnect', function () { console.log('disconnect'); }); socket.on('message', function (msg) { console.log(msg) }); });</script> 配置 nginx如果你用的 swoole 版本为 4.4.4,那么还需要对上篇文章中的 nginx 配置做个小修改,即添加一行: 1proxy_http_version 1.1; 完整配置就不贴了,把这一行加在 proxy_pass http://swoole-http$suffix; 这一行前后都行。 如果没有这一行的话,而且怎么也没反应的话,记得去 log 中找具体报错。 emmm,文档没写,在 issue 里扒出来的……

2019-04-09
laravel 队列学习
学习下 laravel 的队列系统。 队列的目的是将耗时的任务延时处理,比如发送邮件,从而大幅度缩短 Web 请求和相应的时间。 常用的队列后台有: Beanstalk,Amazon SQS,Redis 等。 配置laravel 为多种队列服务做了统一的API,在配置文件 config/queue.php 中可以找到每种队列驱动的配置。 其中每种驱动都有一个默认的 queue 属性,用来存放使用时没有显示定义队列的任务。 12345// 分发到默认队列Job::dispatch();// 分发到 emails 队列Job::dispatch()->onQueue('emails'); 在项目的配置文件中,可以指定驱动,老版本中为 QUEUE_DRIVER,新版本中为QUEUE_CONNECTION , 驱动默认为 sync,这是一个本地的同步驱动,方便调试队列里的任务。 先以 redis 为例做一个邮件发送队列。 因为 laravel 的 redis 默认使用了 predis,所以先装下扩展: 1composer require 'predis/predis' 邮件配置,最后两项是手动添加的,否则会报错: 12345678MAIL_DRIVER=smtpMAIL_HOST=smtp.mxhichina.comMAIL_PORT=25MAIL_USERNAME=m@m-finder.comMAIL_PASSWORD=xxxxxxMAIL_ENCRYPTION=nullMAIL_FROM_NAME=M-finderMAIL_FROM_ADDRESS=m@m-finder.com 生成任务类命令行执行:php artisan make:job EmailJob,该命令会在 app/jobs 下自动创建文件。 在任务类中发送邮件: 12345678public function handle() { $email = $this->email; $content = '这是一封来自Laravel的队列测试邮件.'; Mail::raw($content, function ($message) use ($email) { $message->subject('[ 测试 ] 测试邮件SendMail - ' . date('Y-m-d H:i:s')); $message->to($email); });} 任务调度之前弄了登录事件和监听,就在监听里去触发吧。 1EmailJob::dispatch($guard->user)->onQueue('emails'); 开启队列1php artisan queue:work --tries=3 --timeout=30 --queue=emails 然后重新登录触发任务。可以看到邮箱已经有了提示: 邮件已经成功发出,接下来就可以在实际的需求中使用了。

2019-08-02
在vue中使用laravel-permission的@can标签
1. 在需要权限校验的 model 中新增方法12345678910public function getAllPermissionsAttribute() { $permissions = $this->getAllPermissions(); $permission_names = []; collect($permissions)->map(function ($permission) use (&$permission_names) { $permission_names[] = $permission->name; }); return $permission_names;} 2. 在 app.blade 中存储当前用户的所有权限1234567<script> @auth window.Permissions = @json(Auth::user()->allPermissions); @else window.Permissions = []; @endauth</script> 3. 新建 vue component123456789<script> export default { methods: { $can(permissionName) { return Permissions.indexOf(permissionName) !== -1; } } };</script> 4. 在 app.js 注册 vue 组件12import auth from './components/AuthComponent';\Vue.mixin(auth); 5. 在vue中使用1<a v-if="$can('admin.admins.delete')" class="text-danger" @click="dataDelete(admin)">

2019-03-15
Laravel 生命周期
Laravel 的生命周期主要分为四个阶段: 加载依赖 创建应用实例 接收请求并响应 请求结束进行回调 这四个阶段都在 index.php 中完成: 1234567891011121314151617181920<?php// 加载依赖require __DIR__.'/../vendor/autoload.php';// 创建应用实例$app = require_once __DIR__.'/../bootstrap/app.php';// 实例化 HTTP 内核$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);// 接收请求,生成响应$response = $kernel->handle( $request = Illuminate\Http\Request::capture());// 发送响应$response->send();// 请求结束,进行回调$kernel->terminate($request, $response); 1. 加载依赖laravel 框架依赖 composer 管理扩展包,通过引入 composer 的自动加载程序,就可以轻松完成扩展加载: 1require __DIR__.'/../vendor/autoload.php'; 2 创建应用实例这一步主要由以下几个小步骤组成: 创建应用实例 完成基础注册 基础绑定 基础服务提供者注册 event log route 核心类别名注册 绑定核心 创建应用实例,由 bootstrap/app.php 完成,然后注册三个核心。 1234567<?php// 第一部分: 创建应用实例$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../'));…… 2.1 完成基础注册应用实例创建后,再来看一下具体是怎么工作的,打开 Illuminate\Foundation\Application,其代码如下: 1234567891011121314public function __construct($basePath = null){ // 应用的路径绑定 if ($basePath) { $this->setBasePath($basePath); } // 将基础绑定注册到容器中,容器名 $this->registerBaseBindings(); // 将基础服务提供者注册到容器 Event、Log、Route $this->registerBaseServiceProviders(); // 将核心类别名注册到容器 $this->registerCoreContainerAliases();} 2.2 内核绑定接着看 bootstrap/app.php 中的代码: 12345678910111213141516171819……// 第二步,内核绑定$app->singleton( Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class);$app->singleton( Illuminate\Contracts\Console\Kernel::class, App\Console\Kernel::class);$app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class); 绑定三个内核,HTTP、Console、Exception内核。 3 接收请求并响应再次回到 index.php,查看请求和响应的相关代码: 1234567891011……$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle( $request = Illuminate\Http\Request::capture());$response->send();…… 这一步也是由几个小步骤组成: 实例化 HTTP 核心 实例化内核 注册中间件到路由 session 共享错误 身份验证请求 …… 请求处理 创建请求实例 处理请求,返回响应 发送响应 3.1 注册中间件到路由在 Illuminate\Contracts\Http\Kernel::class 类的构造方法中,将在 HTTP 内核定义的「中间件」注册到路由,注册完后就可以在实际处理 HTTP 请求前调用这些「中间件」实现过滤请求的目的。 1234567891011121314151617181920212223242526272829303132protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class,];public function __construct(Application $app, Router $router){ $this->app = $app; $this->router = $router; $this->syncMiddlewareToRouter();}// 注册中间件到路由protected function syncMiddlewareToRouter(){ $this->router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $this->router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $this->router->aliasMiddleware($key, $middleware); }} 3.2 处理请求处理请求实际包含两个阶段: 创建请求实例 处理请求 3.2.1 创建请求实例通过 Symfony 实例创建一个 Laravel 请求实例。 12345678910111213141516171819202122public static function capture(){ static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals());}public static function createFromBase(SymfonyRequest $request){ $newRequest = (new static)->duplicate( $request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all() ); $newRequest->headers->replace($request->headers->all()); $newRequest->content = $request->content; $newRequest->request = $newRequest->getInputSource(); return $newRequest;} 3.2.2 处理请求在 HTTP 核心的 handdle 方法内,接收一个请求,也就是上一步创建的请求实例,最终生成一个响应。 主要步驟如下: 注册请求到容器 运行引导程序 环境检测,将 env 中的配置读取到变量中 配置文件加载 加载异常处理 注册门面 注册服务提供者 服务启动 发送请求到路由 查找路由 运行控制器或匿名函数 返回响应 HTTP 核心的 handle 方法: 123456789101112131415161718public function handle($request){ try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new RequestHandled($request, $response) ); return $response;} 再往下深入,查看 $response = $this->sendRequestThroughRouter($request); 的具体实现: 12345678910111213141516protected function sendRequestThroughRouter($request){ // 将请求注册到容器 $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); // 启动引导程序 $this->bootstrap(); // 发送请求至路由 return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());} 首先,将 request 注册到容器内,然后清除掉之前的 request 实例缓存,启动引导程序,然后将请求发送到路由。 接下来,看一下引导程序是做什么的: 12345678910111213141516171819202122232425262728293031323334protected $bootstrappers = [ \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, \Illuminate\Foundation\Bootstrap\RegisterProviders::class, \Illuminate\Foundation\Bootstrap\BootProviders::class,];public function bootstrap(){ if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); }}protected function bootstrappers(){ return $this->bootstrappers;}// src/Illuminate/Foundation/Application.phppublic function bootstrapWith(array $bootstrappers){ $this->hasBeenBootstrapped = true; foreach ($bootstrappers as $bootstrapper) { $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]); $this->make($bootstrapper)->bootstrap($this); $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]); }} 在容器内的具体实现方法中,会先解析引导程序,然后再通过调用引导程序的 bootstrap 方法来启动服务。引导程序功能: 环境检测,将 env 配置文件载入到 $_ENV 变量中 加载配置文件 加载异常处理 加载 Facades,注册完成后可以用别名的方式访问具体的类 注册服务提供者,在这里我们会将配置在 app.php 文件夹下 providers 节点的服务器提供者注册到 APP 容器,供请求处理阶段使用 服务启动 在发送请求至路由这行代码中,完成了:管道(pipeline)创建、将 request 传入管道、对 request 执行中间件处理和实际的请求处理四个不同的操作。 1234return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); 继续深入 $this->dispatchToRouter(),分析程序是如何处理请求的: 注册请求 查找路由 运行控制器 返回响应结果 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364protected function dispatchToRouter(){ return function ($request) { // 将请求注册到容器 $this->app->instance('request', $request); return $this->router->dispatch($request); };}public function dispatch(Request $request){ $this->currentRequest = $request; return $this->dispatchToRoute($request);}public function dispatchToRoute(Request $request){ return $this->runRoute($request, $this->findRoute($request));}// 查找路由protected function findRoute($request){ $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route;}protected function runRoute(Request $request, Route $route){ $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) );}protected function runRouteWithinStack(Route $route, Request $request){ $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); // 返回运行结果 return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { // 运行匹配到的路由控制器或匿名函数 return $this->prepareResponse( $request, $route->run() ); });} 执行 $route->run() 的方法定义在 Illuminate\Routing\Route 类中: 1234567891011121314public function run(){ $this->container = $this->container ?: new Container; try { if ($this->isControllerAction()) { return $this->runController(); } return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); }} 如果路由的实现是一个控制器,会完成控制器实例化并执行指定方法;如果是一个匿名函数就会直接调用。最终响应通过 prepareResponse 返回。 3.2.3 发送响应绕了一大圈,最后终于回到了开始的地方 12// 发送响应$response->send(); 最终发送,由 src/Illuminate/Http/Response.php 的父类 Symfony\Component\HttpFoundation\Response 完成: 12345678910111213public function send(){ $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); } return $this;} 4 请求结束,进行回调1$kernel->terminate($request, $response); 继续往下看,核心的 terminate 方法: 123456public function terminate($request, $response){ $this->terminateMiddleware($request, $response); $this->app->terminate();} terminateMiddleware 中,进行终止中间件,$this->app->terminate() 终止程序。 总结创建应用实例,完成项目路径注册、基础服务注册、核心类别名注册,然后将 HTTP 和 Console, Exception 核心注册到容器。 然后再实例化内核,将中间件加载到路由,再将请求注册到容器,然后运行引导程序,进行环境检测、加载系统配置等系统环境配置。 然后进行中间件校验,通过校验后才会最终处理实际的控制器或匿名函数并生成响应。 最终,发送响应给用户,清理项目中的中间件,完成一个请求周期。

2020-01-16
win10 子系统(wsl2)运行 laradock
没忍住,把家里的电脑更新到了预览版,然后体验了一下 wls2,相对于 wsl1,使用 docker 简直不要太简单。 更新 wsl2开个命令行: 12wsl --set-version Ubuntu 2wsl --set-default-version 2 下载docker wsl2 版 docker 下载 配置下载完成后,先勾选 wsl2。 然后直接重启 docker。 重启完成后,勾选你的发行版子系统。 然后再次重启 docker。 测试12bashdocker -v 重装好像把我之前的镜像都给弄没了,切到 laradock 重新下载。 正常访问,打完收工。

2019-10-28
在 laravel vue 中使用 svg 图标
在 laravel vue 中使用 svg 图标 安装扩展组件:123npm install laravel-mix-svg-vueoryarn add laravel-mix-svg-vue 在 webpack.mix.js 中添加引用:12345const mix = require('laravel-mix');require('laravel-mix-svg-vue');mix.js('resources/js/app.js', 'public/js') .svgVue(); 在 app.js 中引用组件1234import Vue from 'vue';import SvgVue from 'svg-vue';Vue.use(SvgVue); svg 使用:1<svg-vue icon="avatar"></svg-vue> 默认配置123456789{ svgPath: 'resources/svg', extract: false, svgoSettings: [ { removeTitle: true }, { removeViewBox: false }, { removeDimensions: true } ]} 参数 类型 默认值 说明 svgPath String resources/svg svg 图标路径 extract Boolean false 将 svg 与主包分离 svgoSettings Array [{ removeTitle: true }, { removeViewBox: false }, { removeDimensions: true }] svgo 相关设置 缺点好像是不能通过参数动态改变 svg 内容,没试出来,不知道什么原因。