您现在的位置是:首页 > 技术杂谈 > Laravel > swoole > php php
前提:配置好环境,安装swooleTW2.5扩展和inotify,如果安装大于swooleTW2.5版本的扩展,就不要直接用下面的文件,单独拷方法即可。
1、配件置文
config目录下的配置文件 swoole_http.php
<?php return [ /* |-------------------------------------------------------------------------- | HTTP server configurations. |-------------------------------------------------------------------------- | | @see https://www.swoole.co.uk/docs/modules/swoole-server/configuration | */ 'server' => [ 'host' => env('SWOOLE_HTTP_HOST', '127.0.0.1'), 'port' => env('SWOOLE_HTTP_PORT', '1215'), 'public_path' => base_path('public'), // Determine if to use swoole to respond request for static files 'handle_static_files' => env('SWOOLE_HANDLE_STATIC', true), // You must add --enable-openssl while compiling Swoole // Put `SWOOLE_SOCK_TCP | SWOOLE_SSL` if you want to enable SSL 'socket_type' => SWOOLE_SOCK_TCP, 'options' => [ 'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')), 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')), 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', true), // Normally this value should be 1~4 times larger according to your cpu cores. 'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num()), 'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num()), 'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num()), // The data to receive can't be larger than buffer_output_size. 'package_max_length' => 20 * 1024 * 1024, // The data to send can't be larger than buffer_output_size. 'buffer_output_size' => 10 * 1024 * 1024, // Max buffer size for socket connections 'socket_buffer_size' => 128 * 1024 * 1024, // Worker will restart after processing this number of requests 'max_request' => 3000, // Enable coroutine send 'send_yield' => true, // You must add --enable-openssl while compiling Swoole 'ssl_cert_file' => null, 'ssl_key_file' => null, ], ], /* |-------------------------------------------------------------------------- | Enable to turn on websocket server. |-------------------------------------------------------------------------- */ 'websocket' => [ 'enabled' => env('SWOOLE_HTTP_WEBSOCKET', false), ], //文件监控热更新目录文件 ext后缀文件更新 'watch' => [ 'file' => [ app_path('business.php'), app_path('common.php'), ], 'dir' => [ app_path('Modules'), app_path('Models'), app_path('Services'), base_path('config'), ], 'ext' => ['php'] ], /* |-------------------------------------------------------------------------- | Console output will be transferred to response content if enabled. |-------------------------------------------------------------------------- */ 'ob_output' => env('SWOOLE_OB_OUTPUT', true), /* |-------------------------------------------------------------------------- | Pre-resolved instances here will be resolved when sandbox created. |-------------------------------------------------------------------------- */ 'pre_resolved' => [ 'view', 'files', 'session', 'session.store', 'routes', 'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie', 'encrypter', 'hash', 'router', 'translator', 'url', 'log', ], /* |-------------------------------------------------------------------------- | Instances here will be cleared on every request. |-------------------------------------------------------------------------- */ 'instances' => [ // ], /* |-------------------------------------------------------------------------- | Providers here will be registered on every request. |-------------------------------------------------------------------------- */ 'providers' => [ Illuminate\Pagination\PaginationServiceProvider::class, ], /* |-------------------------------------------------------------------------- | Resetters for sandbox app. |-------------------------------------------------------------------------- */ 'resetters' => [ SwooleTW\Http\Server\Resetters\ResetConfig::class, SwooleTW\Http\Server\Resetters\ResetSession::class, SwooleTW\Http\Server\Resetters\ResetCookie::class, SwooleTW\Http\Server\Resetters\ClearInstances::class, SwooleTW\Http\Server\Resetters\BindRequest::class, SwooleTW\Http\Server\Resetters\RebindKernelContainer::class, SwooleTW\Http\Server\Resetters\RebindRouterContainer::class, SwooleTW\Http\Server\Resetters\RebindViewContainer::class, SwooleTW\Http\Server\Resetters\ResetProviders::class, ], /* |-------------------------------------------------------------------------- | Define your swoole tables here. | | @see https://www.swoole.co.uk/docs/modules/swoole-table |-------------------------------------------------------------------------- */ 'tables' => [ // 'table_name' => [ // 'size' => 1024, // 'columns' => [ // ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], // ] // ], ], ];
vendor/swooletw/laravel-swoole/config目录下的配置文件 swoole_http.php
<?php use Swoole\Table; return [ /* |-------------------------------------------------------------------------- | HTTP server configurations. |-------------------------------------------------------------------------- | | @see https://www.swoole.co.uk/docs/modules/swoole-server/configuration | */ 'server' => [ 'host' => env('SWOOLE_HTTP_HOST', '127.0.0.1'), 'port' => env('SWOOLE_HTTP_PORT', '1215'), 'public_path' => base_path('public'), // Determine if to use swoole to respond request for static files 'handle_static_files' => env('SWOOLE_HANDLE_STATIC', true), // You must add --enable-openssl while compiling Swoole // Put `SWOOLE_SOCK_TCP | SWOOLE_SSL` if you want to enable SSL 'socket_type' => SWOOLE_SOCK_TCP, 'options' => [ 'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')), 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')), 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', false), // Normally this value should be 1~4 times larger according to your cpu cores. 'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num()), 'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num()), 'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num()), // The data to receive can't be larger than buffer_output_size. 'package_max_length' => 20 * 1024 * 1024, // The data to send can't be larger than buffer_output_size. 'buffer_output_size' => 10 * 1024 * 1024, // Max buffer size for socket connections 'socket_buffer_size' => 128 * 1024 * 1024, // Worker will restart after processing this number of requests 'max_request' => 3000, // Enable coroutine send 'send_yield' => true, // You must add --enable-openssl while compiling Swoole 'ssl_cert_file' => null, 'ssl_key_file' => null, ], ], /* |-------------------------------------------------------------------------- | Enable to turn on websocket server. |-------------------------------------------------------------------------- */ 'websocket' => [ 'enabled' => env('SWOOLE_HTTP_WEBSOCKET', false), ], 'watch' => [ 'file' => [ ], 'dir' => [ ], 'ext' => [] ], /* |-------------------------------------------------------------------------- | Console output will be transferred to response content if enabled. |-------------------------------------------------------------------------- */ 'ob_output' => env('SWOOLE_OB_OUTPUT', true), /* |-------------------------------------------------------------------------- | Pre-resolved instances here will be resolved when sandbox created. |-------------------------------------------------------------------------- */ 'pre_resolved' => [ 'view', 'files', 'session', 'session.store', 'routes', 'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie', 'encrypter', 'hash', 'router', 'translator', 'url', 'log', ], /* |-------------------------------------------------------------------------- | Instances here will be cleared on every request. |-------------------------------------------------------------------------- */ 'instances' => [ // ], /* |-------------------------------------------------------------------------- | Providers here will be registered on every request. |-------------------------------------------------------------------------- */ 'providers' => [ Illuminate\Pagination\PaginationServiceProvider::class, ], /* |-------------------------------------------------------------------------- | Resetters for sandbox app. |-------------------------------------------------------------------------- */ 'resetters' => [ SwooleTW\Http\Server\Resetters\ResetConfig::class, SwooleTW\Http\Server\Resetters\ResetSession::class, SwooleTW\Http\Server\Resetters\ResetCookie::class, SwooleTW\Http\Server\Resetters\ClearInstances::class, SwooleTW\Http\Server\Resetters\BindRequest::class, SwooleTW\Http\Server\Resetters\RebindKernelContainer::class, SwooleTW\Http\Server\Resetters\RebindRouterContainer::class, SwooleTW\Http\Server\Resetters\RebindViewContainer::class, SwooleTW\Http\Server\Resetters\ResetProviders::class, ], /* |-------------------------------------------------------------------------- | Define your swoole tables here. | | @see https://www.swoole.co.uk/docs/modules/swoole-table |-------------------------------------------------------------------------- */ 'tables' => [ // 'table_name' => [ // 'size' => 1024, // 'columns' => [ // ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], // ] // ], ] ];
2、Manager.php
位置:vendor/swooletw/laravel-swoole/src/Server
<?php namespace SwooleTW\Http\Server; use Throwable; use Swoole\Http\Server; use SwooleTW\Http\Server\Sandbox; use SwooleTW\Http\Task\SwooleTaskJob; use Illuminate\Support\Facades\Facade; use SwooleTW\Http\Websocket\Websocket; use SwooleTW\Http\Transformers\Request; use SwooleTW\Http\Transformers\Response; use SwooleTW\Http\Concerns\WithApplication; use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Debug\ExceptionHandler; use SwooleTW\Http\Concerns\InteractsWithWebsocket; use SwooleTW\Http\Concerns\InteractsWithSwooleTable; class Manager { use InteractsWithWebsocket, InteractsWithSwooleTable, WithApplication; /** * Container. * * @var \Illuminate\Contracts\Container\Container */ protected $container; /** * @var string */ protected $framework; /** * @var string */ protected $basePath; protected $fileExt; /** * Server events. * * @var array */ protected $events = [ 'start', 'shutDown', 'workerStart', 'workerStop', 'packet', 'bufferFull', 'bufferEmpty', 'task', 'finish', 'pipeMessage', 'workerError', 'managerStart', 'managerStop', 'request', ]; /** * HTTP server manager constructor. * * @param \Swoole\Http\Server $server * @param \Illuminate\Contracts\Container\Container $container * @param string $framework * @param string $basePath */ public function __construct(Container $container, $framework, $basePath = null) { $this->container = $container; $this->setFramework($framework); $this->setBasepath($basePath); $this->initialize(); } /** * Run swoole server. */ public function run() { $this->container['swoole.server']->start(); } /** * Stop swoole server. */ public function stop() { $this->container['swoole.server']->shutdown(); } /** * Initialize. */ protected function initialize() { $this->createTables(); $this->prepareWebsocket(); $this->setSwooleServerListeners(); } /** * Set swoole server listeners. */ protected function setSwooleServerListeners() { foreach ($this->events as $event) { $listener = 'on' . ucfirst($event); if (method_exists($this, $listener)) { $this->container['swoole.server']->on($event, [$this, $listener]); } else { $this->container['swoole.server']->on($event, function () use ($event) { $event = sprintf('swoole.%s', $event); $this->container['events']->dispatch($event, func_get_args()); }); } } } /** * "onStart" listener. */ public function onStart() { $this->setProcessName('master process'); $this->createPidFile(); $this->container['events']->dispatch('swoole.start', func_get_args()); } /** * The listener of "managerStart" event. * * @return void */ public function onManagerStart() { $this->setProcessName('manager process'); $this->container['events']->dispatch('swoole.managerStart', func_get_args()); } protected function watch($server){ $notify = inotify_init(); $watch = $this->container['config']->get('swoole_http.watch'); $dir = $watch['dir']; $list = $watch['file']; $this->fileExt = $watch['ext']; $this->getDir($dir,$list); foreach ($list as $item) { inotify_add_watch($notify, $item, IN_CREATE | IN_DELETE | IN_MODIFY); } swoole_event_add($notify, function () use ($notify, $server) { $events = inotify_read($notify); if (!empty($events)) { echo "Hot update success!\n"; $server->reload(); } }); } protected function getDir($dir,&$list){ foreach ($dir as $dr) { foreach (array_diff(scandir($dr), array('.', '..')) as $item) { if(is_dir($dr . '/' . $item)){ $dirArr = [$dr . '/' . $item]; $this->getDir($dirArr,$list); }else{ $ext = pathinfo($dr . '/' . $item, PATHINFO_EXTENSION); if(in_array($ext,$this->fileExt)){ $list[] = $dr . '/' . $item; } } } } } /** * "onWorkerStart" listener. */ public function onWorkerStart($server) { $this->clearCache(); $this->setProcessName('worker process'); $this->container['events']->dispatch('swoole.workerStart', func_get_args()); // don't init laravel app in task workers if ($server->taskworker) { return; } echo date('Y-m-d'); $this->watch($server); // clear events instance in case of repeated listeners in worker process Facade::clearResolvedInstance('events'); // prepare laravel app $this->getApplication(); // bind after setting laravel app $this->bindToLaravelApp(); // prepare websocket handler and routes if ($this->isWebsocket) { $this->prepareWebsocketHandler(); $this->loadWebsocketRoutes(); } } /** * "onRequest" listener. * * @param \Swoole\Http\Request $swooleRequest * @param \Swoole\Http\Response $swooleResponse */ public function onRequest($swooleRequest, $swooleResponse) { $this->app['events']->dispatch('swoole.request'); $this->resetOnRequest(); $handleStatic = $this->container['config']->get('swoole_http.handle_static_files', true); $publicPath = $this->container['config']->get('swoole_http.server.public_path', base_path('public')); try { // handle static file request first if ($handleStatic && Request::handleStatic($swooleRequest, $swooleResponse, $publicPath)) { return; } // transform swoole request to illuminate request $illuminateRequest = Request::make($swooleRequest)->toIlluminate(); // set current request to sandbox $this->app['swoole.sandbox']->setRequest($illuminateRequest); // enable sandbox $this->app['swoole.sandbox']->enable(); // handle request via laravel/lumen's dispatcher $illuminateResponse = $this->app['swoole.sandbox']->run($illuminateRequest); $response = Response::make($illuminateResponse, $swooleResponse); $response->send(); } catch (Throwable $e) { try { $exceptionResponse = $this->app[ExceptionHandler::class]->render($illuminateRequest, $e); $response = Response::make($exceptionResponse, $swooleResponse); $response->send(); } catch (Throwable $e) { $this->logServerError($e); } } finally { // disable and recycle sandbox resource $this->app['swoole.sandbox']->disable(); } } /** * Reset on every request. */ protected function resetOnRequest() { // Reset websocket data if ($this->isWebsocket) { $this->app['swoole.websocket']->reset(true); } } /** * Set onTask listener. */ public function onTask($server, $taskId, $srcWorkerId, $data) { $this->container['events']->dispatch('swoole.task', func_get_args()); try { // push websocket message if (is_array($data)) { if ($this->isWebsocket && array_key_exists('action', $data) && $data['action'] === Websocket::PUSH_ACTION) { $this->pushMessage($server, $data['data'] ?? []); } // push async task to queue } elseif (is_string($data)) { $decoded = json_decode($data, true); if (JSON_ERROR_NONE === json_last_error() && isset($decoded['job'])) { (new SwooleTaskJob($this->container, $server, $data, $taskId, $srcWorkerId))->fire(); } } } catch (Throwable $e) { $this->logServerError($e); } } /** * Set onFinish listener. */ public function onFinish($server, $taskId, $data) { // task worker callback return; } /** * Set onShutdown listener. */ public function onShutdown() { $this->removePidFile(); } /** * Set bindings to Laravel app. */ protected function bindToLaravelApp() { $this->bindSandbox(); $this->bindSwooleTable(); if ($this->isWebsocket) { $this->bindRoom(); $this->bindWebsocket(); } } /** * Bind sandbox to Laravel app container. */ protected function bindSandbox() { $this->app->singleton(Sandbox::class, function ($app) { return new Sandbox($app, $this->framework); }); $this->app->alias(Sandbox::class, 'swoole.sandbox'); } /** * Gets pid file path. * * @return string */ protected function getPidFile() { return $this->container['config']->get('swoole_http.server.options.pid_file'); } /** * Create pid file. */ protected function createPidFile() { $pidFile = $this->getPidFile(); $pid = $this->container['swoole.server']->master_pid; file_put_contents($pidFile, $pid); } /** * Remove pid file. */ protected function removePidFile() { $pidFile = $this->getPidFile(); if (file_exists($pidFile)) { unlink($pidFile); } } /** * Clear APC or OPCache. */ protected function clearCache() { if (function_exists('apc_clear_cache')) { apc_clear_cache(); } if (function_exists('opcache_reset')) { opcache_reset(); } } /** * Set process name. * * @codeCoverageIgnore * @param $process */ protected function setProcessName($process) { // MacOS doesn't support modifying process name. if ($this->isMacOS() || $this->isInTesting()) { return; } $serverName = 'swoole_http_server'; $appName = $this->container['config']->get('app.name', 'Laravel'); $name = sprintf('%s: %s for %s', $serverName, $process, $appName); swoole_set_process_name($name); } /** * Indicates if the process is running in macOS. * * @return bool */ protected function isMacOS() { return PHP_OS === 'Darwin'; } /** * Indicates if it's in phpunit environment. * * @return bool */ protected function isInTesting() { return defined('IN_PHPUNIT') && IN_PHPUNIT; } /** * Log server error. * * @param Throwable */ public function logServerError(Throwable $e) { $this->container[ExceptionHandler::class]->report($e); } }
注意点:laravel5.8+swooleTW2.5版本下不支持fire方法,将所有的fire方法改为dispatch,上面文件已修改完。
3、Manager.php的关键点
1) //监控方法,在onStartWork中调用,有变化的文件会执行reload方法重载更新
protected function watch($server){
$notify = inotify_init();
$watch = $this->container['config']->get('swoole_http.watch');
$dir = $watch['dir'];
$list = $watch['file'];
$this->fileExt = $watch['ext'];
$this->getDir($dir,$list);
foreach ($list as $item) {
inotify_add_watch($notify, $item, IN_CREATE | IN_DELETE | IN_MODIFY);
}
swoole_event_add($notify, function () use ($notify, $server) {
$events = inotify_read($notify);
if (!empty($events)) {
echo "Hot update success!\n";
$server->reload();
}
});
}
2)//获取配置文件热更新目录下的所有文件路径
protected function getDir($dir,&$list){
foreach ($dir as $dr) {
foreach (array_diff(scandir($dr), array('.', '..')) as $item) {
if(is_dir($dr . '/' . $item)){
$dirArr = [$dr . '/' . $item];
$this->getDir($dirArr,$list);
}else{
$ext = pathinfo($dr . '/' . $item, PATHINFO_EXTENSION);
if(in_array($ext,$this->fileExt)){
$list[] = $dr . '/' . $item;
}
}
}
}
}
文章评论

评论列表