LaravelDcatAdminGateWayWorkerWorkermanSwooleOctane实现一键部署
再开始前我默认你已经安装了Swoole扩展,其他扩展可以composer安装
composer require dcat/laravel-admin
composer require laravel/horizon
composer require laravel/octane
composer require workerman/gateway-worker
首先咱们要借助Laravel强大的Console模块,创建文件夹如下:
app/Console/Commands/Worker
文件夹里创建2个文件Events.php 和 Workers.php
Workers.php
<?php
namespace App\Console\Commands\Worker;
use App\Utils\ConsoleUtil;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Workerman\Crontab\Crontab;
use Workerman\Events\Fiber;
use Workerman\Events\Swoole;
use Workerman\Timer;
use Workerman\Worker;
class Workers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'workers {worker_command} {--daemon} {--d}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'WorkerMan Server for Laravel. Start E.g "php artisan workers restart --daemon"';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
public function handle(): void
{
if (str_starts_with(PHP_OS, 'WIN')) {
$this->output->caution('GatewayWorker Not Support On Windows.');
}
global $argv;// php artisan workers restart -d
$command = $this->argument('worker_command');
if (!in_array($command, ['start', 'stop', 'reload', 'restart', 'status', 'connections'])) {
$this->output->caution("$command Is Invalid command");
}
$argv[1] = $command;
if (!empty($this->option('daemon')) || !empty($this->option('d'))) {
$argv[2] = '-d';
if (count($argv) == 5) array_pop($argv);
} else {
// 配置 Laravel Workers 只有非守护进程(supervisor守护服务时使用)下才启动laravel相关进程
$this->laravelWorker();
}
array_pop($argv);
$this->start();
}
public function laravelWorker(): void
{
$laravel_workers = [
1 => 'LaravelSchedule',
2 => 'LaravelHttpServe',// 本地启动serve 正式启动 octane:start
0 => 'LaravelHorizon',
];
$laravel_worker = new Worker();
$laravel_worker->count = count($laravel_workers);
$laravel_worker->name = "LaravelWorker";
//$laravel_worker->eventLoop = Swoole::class; // 使用Swoole协程
$laravel_worker->onWorkerStart = function () use (&$laravel_worker, $laravel_workers) {
$laravel_worker->name = $laravel_workers[$laravel_worker->id];
ConsoleUtil::instance()->output("onWorkerStart:" . $laravel_worker->id . " " . $laravel_worker->name);
match ($laravel_worker->id) {
1 => $this->call('schedule:work'),
2 => $this->call(App::environment('local') ? 'serve' : 'octane:start'),// serve octane:start
default => $this->call('horizon'),
};
};
$laravel_worker->onWorkerStop = function ($worker) {
ConsoleUtil::instance()->output("onWorkerStop:" . $worker->id . " " . $worker->name);
match ($worker->id) {
1 => null,
2 => $this->call('octane:stop'),
default => $this->call('horizon:terminate'),
};
};
$laravel_worker->onWorkerReload = function ($worker) {
ConsoleUtil::instance()->output("onWorkerReload:" . $worker->id . " " . $worker->name);
match ($worker->id) {
1 => null,
2 => $this->call('octane:stop'),
default => $this->call('horizon:terminate'),
};
};
$laravel_worker->onError = function ($worker, $code, $msg) {
ConsoleUtil::instance()->output("onError:" . $worker->id . " " . $worker->name . " " . $code . " " . $msg);
match ($worker->id) {
1 => null,
2 => $this->call('octane:stop'),
default => $this->call('horizon:terminate'),
};
};
}
/**
* 启动
* @return void
*/
public function start(): void
{
$option = Config::get('gateway');
$register_address = !empty($option['register_address']) ? $option['register_address'] : '127.0.0.1:1236';
if (!empty($option['register_deploy'])) {
// 分布式部署的时候其它服务器可以关闭register服务
// 注意需要设置不同的lanIp
$this->register($register_address);
}
// 启动businessWorker
if (!empty($option['businessWorker_deploy'])) {
$this->businessWorker($register_address, $option['businessWorker'] ?? []);
}
// 启动gateway
if (!empty($option['gateway_deploy'])) {
$this->gateway($register_address, $option);
}
//启动RobotWorker
$this->robotWorker();
Worker::$pidFile = storage_path('app') . '/gateway.pid';
Worker::$logFile = storage_path('logs') . '/worker_man.log';
Worker::runAll();
}
/**
* 启动register
* @access public
* @param string $registerAddress
* @return void
*/
public function register(string $registerAddress): void
{
// 初始化register
$worker = new Register('text://' . $registerAddress);
//$worker->eventLoop = Fiber::class; // 使用自带的Fiber协程
$worker->eventLoop = Swoole::class; // 使用Swoole协程
}
/**
* 启动businessWorker
* @access public
* @param string $registerAddress registerAddress
* @param array $option 参数
* @return void
*/
public function businessWorker(string $registerAddress, array $option = []): void
{
// 初始化 businessWorker 进程
$worker = new BusinessWorker();
$this->setOption($worker, $option);
$worker->registerAddress = $registerAddress;
}
/**
* 启动gateway
* @param string $register_address registerAddress
* @param array $option 参数
* @return void
*/
public function gateway(string $register_address, array $option = []): void
{
// 初始化 gateway 进程
if (!empty($option['socket'])) {
$socket = $option['socket'];
unset($option['socket']);
} else {
$protocol = !empty($option['protocol']) ? $option['protocol'] : 'websocket';
$socket = $protocol . '://' . $option['host'] . ':' . $option['port'];
unset($option['host'], $option['port'], $option['protocol']);
}
$gateway = new Gateway($socket, $option['context'] ?? []);
// 以下设置参数都可以在配置文件中重新定义覆盖
$gateway->name = 'Gateway';
$gateway->count = 4;
$gateway->lanIp = '127.0.0.1';
$gateway->startPort = 2000;
$gateway->pingInterval = 30;
$gateway->pingNotResponseLimit = 0;
$gateway->pingData = '{"type":"ping"}';
$gateway->registerAddress = $register_address;
// 全局静态属性设置
foreach ($option as $name => $val) {
if (in_array($name, ['stdoutFile', 'daemonize', 'pidFile', 'logFile'])) {
Worker::${$name} = $val;
unset($option[$name]);
}
}
$this->setOption($gateway, $option);
}
public function robotWorker(): void
{
$worker = new Worker();
$worker->name = 'RobotWorker';
$worker->onWorkerStart = function () use ($worker) {
ConsoleUtil::instance()->output("onWorkerStart:{$worker->id} {$worker->name}");
Timer::add(60, function () use ($worker) {
//ConsoleUtil::instance()->output("{$worker->name} 60s 执行一次。");
});
};
}
/**
* 设置参数
* @access protected
* @param Worker $worker Worker对象
* @param array $option 参数
* @return void
*/
protected function setOption(Worker $worker, array $option = []): void
{
// 设置参数
if (!empty($option)) {
foreach ($option as $key => $val) {
$worker->$key = $val;
}
}
}
}
Events.php
<?php
namespace App\Console\Commands\Worker;
use App\Models\User;
use App\Utils\ConsoleUtil;
use Exception;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Lib\Gateway;
/**
* 推送主逻辑
* 主要是处理 onMessage onClose
*/
class Events
{
/**
*
* 当businessWorker进程启动时触发。每个进程生命周期内都只会触发一次。
* @param BusinessWorker $businessWorker
*/
public static function onWorkerStart(BusinessWorker $businessWorker)
{
}
/**
* 当有客户端连接时,将client_id返回,让mvc框架判断当前uid并执行绑定
* @param string $client_id
*/
public static function onConnect(string $client_id): void
{
//$_SERVER:{"REMOTE_ADDR":"127.0.0.1","REMOTE_PORT":38508,"GATEWAY_ADDR":"127.0.0.1","GATEWAY_PORT":8282,"GATEWAY_CLIENT_ID":"7f00000108fc00000002"}
}
/**
* @param string $client_id client_id固定为20个字符的字符串,用来全局标记一个socket连接,每个客户端连接都会被分配一个全局唯一的client_id。
* @param array $data websocket握手时的http头数据,包含get、server等变量 $data['get']['token'] $data['server']['SERVER_NAME'] $data['cookie']['uid']
*/
public static function onWebSocketConnect(string $client_id, array $data): void
{
$client_ip = get_client_ip($data['server']);
$_SERVER['REMOTE_ADDR'] = $client_ip;//修正客户ip
//ConsoleUtil::instance()->output("ip:" . json_encode(get_client_ips($data['server'])) . " " . json_encode($data) . " " . json_encode($_SERVER));
Gateway::sendToClient($client_id, json_encode([
'm' => 'Lobby',
'f' => 'init',
'd' => [
'type' => 'init',
'client_id' => $client_id,
'client_ip' => $client_ip,
]
]));
}
/**
* 服务端有消息时
* @param string $client_id
* @param mixed $message
* @throws Exception
*/
public static function onMessage(string $client_id, mixed $message): void
{
//ConsoleUtil::instance()->output($message);
// 客户端传递的是json数据
$message = json_decode($message, true);
if (!$message) {
return;
}
// 检测用户状态
$user_id = $_SESSION['user_id'] ?? 0;
if ($message['f'] != 'bind') {
if (!$user_id) return;
if (User::query()->find($user_id)->status < 1) return;
}
// 根据类型执行不同的业务
switch ($message['f']) {
// 客户端回应服务端的心跳
case 'pong':
break;
case 'ping':
self::sendStatus($client_id);
break;
case 'bind':
self::auth($client_id, $message);
break;
case 'simple':
Gateway::sendToUid($message['d']['to_user_id'], json_encode($message['d'], JSON_UNESCAPED_UNICODE));
break;
case 'room':
Gateway::sendToGroup($message['d']['room_id'], json_encode($message['d'], JSON_UNESCAPED_UNICODE));
//Gateway::sendToAll(json_encode($message, JSON_UNESCAPED_UNICODE));
break;
case 'read_message':
break;
}
}
/**
* 回复 Ping
* @param string $client_id
* @return bool
*/
protected static function sendStatus(string $client_id): bool
{
$uid = $_SESSION['user_id'] ?? 0;
$multi_port = false;
if ($uid) {
$arr = Gateway::getClientIdByUid($uid);
if (count($arr) > 1) {
$multi_port = true;
}
}
return Gateway::sendToClient($client_id, json_encode([
'm' => 'Lobby',
'f' => 'pong',
'd' => ['type' => 'pong', 'multi_port' => $multi_port,]
]));
}
/**
* 验证用户的真实性并绑定
* @param string $client_id
* @param array $message
* @return bool
*/
protected static function auth(string $client_id, array $message): bool
{
try {
$d = $message['d'] ?? [];
$user_id = (int)($d['user_id'] ?? 0);
$room_id = (int)(empty($d['room_id']) ? 1 : $d['room_id']);
if ($user_id <= 0) {
return Gateway::closeClient($client_id);
}
$user = User::query()->find($user_id);
if (is_null($user)) return Gateway::closeClient($client_id);
$user->ws_online = 1;
$user->save();
$_SESSION['user_id'] = $user_id;
Gateway::bindUid($client_id, $user_id);
if ($room_id > 0) {
Gateway::joinGroup($client_id, $room_id);
}
//测试消息
// 跑马灯 就是content内容 推全量用户 不存数据库
// $message = "Player8152064 successfully recharge ₹137.00(跑马灯你只需拿这个content显示即可,和接口不一样)";
// (new MessageService())->setF('notify_lamp')->sendToGroup(1, $message);
// // 30分钟奖励弹窗 发给个人的 存库
// (new MessageService())->from(1)->setF('active_reward')
// ->setMessage([
// 'msg_type' => "active_reward",
// 'ext' => [
// ['amount' => 1000, 'id' => 123, 'key' => 'lucky_30_minutes', 'status' => 0, 'user_id' => 30],
// ['amount' => 1000, 'id' => 123, 'key' => 'lucky_monday_bonus', 'status' => 0, 'user_id' => 30]
// ],
// ])
// ->sendToUid($user_id, "这是活动弹窗消息类型,拿ext数据,是一个列表关键字段和/api/activity_user_logs保持一致");
//
// //余额变动提醒
// (new MessageService())->from(1)->setF('user_balance')
// ->setMessage([
// 'msg_type' => 'bill_1',
// 'ext' => ['balance' => 1300]
// ])->sendToUid($user_id, "这是余额变动提醒消息的结构类型,拿ext里的balance");
return self::sendStatus($client_id);
} catch (Exception $exception) {
return Gateway::closeClient($client_id);
}
}
/**
* 断开连接
* @param $client_id
* @return bool
*/
protected static function closeClient($client_id): bool
{
$_SESSION['user_id'] = null;
return Gateway::closeClient($client_id);
}
/**
* 当断开连接时
* @param string $client_id
* @throws Exception
*/
public static function onClose(string $client_id): void
{
$user_id = $_SESSION['user_id'] ?? false;
if ($user_id) {
Gateway::sendToAll(json_encode([
'm' => 'Lobby',
'f' => 'isOnline',
'd' => [
'type' => 'isOnline',
'user_id' => $user_id,
'client_id' => $client_id,
'time' => time(),
'is_online' => 0,
]
]));
$user = User::query()->find($user_id);
$user->ws_online = 0;
$user->save();
}
}
/**
* 当businessWorker进程退出时触发。每个进程生命周期内都只会触发一次。
* 可以在这里为每一个businessWorker进程做一些清理工作,例如保存一些重要数据等。
* @param BusinessWorker $businessWorker
*/
public static function onWorkerStop(BusinessWorker $businessWorker)
{
}
}
config/gateway.php
<?php
return [
'php_bin' => env('PHP_BIN', 'php'),
// 一些自定义配置
'register_deploy' => env('REGISTER_DEPLOY', true), // 是否需要部署register
'businessWorker_deploy' => true, // 是否需要部署businessWorker
'gateway_deploy' => true, // 是否需要部署gateway
// Register配置
'register_address' => env('REGISTER_ADDRESS', '127.0.0.1:1236'),
// BusinessWorker配置
'businessWorker' => [
'name' => 'BusinessWorker',
'count' => env('BUSINESS_WORKER_COUNT', 1),
'eventHandler' => \App\Console\Commands\Worker\Events::class,
],
// Gateway配置
'protocol' => 'Websocket', // 协议 支持 tcp udp unix http websocket text
'host' => '0.0.0.0', // 监听地址
'port' => env('GATEWAY_PORT', 8282), // 监听端口
'socket' => '', // 完整监听地址
'name' => env('GATEWAY_WORKER_NAME', 'gatewayWorker'),
'count' => env('GATEWAY_WORKER_COUNT', 1),
'lanIp' => env('GATEWAY_WORKER_LAN_IP', '127.0.0.1'),
'startPort' => env('GATEWAY_WORKER_START_PORT', 2300),
'daemonize' => false,
'pingInterval' => 30,//心跳间隔
'pingNotResponseLimit' => 1,
'pingData' => '{"m": "Lobby", "f": "ping", "d": null}',//{"type":"ping"}
];
config/horizon.php
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Horizon will be accessible from. If this
| setting is null, Horizon will reside under the same domain as the
| application. Otherwise, this value will serve as the subdomain.
|
*/
'domain' => env('HORIZON_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
'path' => env('HORIZON_PATH', 'horizon'),
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
'use' => 'queue',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
'prefix' => env(
'HORIZON_PREFIX',
Str::slug(env('APP_NAME', 'laravel'), '_') . '_horizon:'
),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| 队列等待时间阈值
|--------------------------------------------------------------------------
|
| 此选项允许您配置何时触发LongWaitDetected事件。在触发此事件之前,每个连接/队列组合都可能有自己唯一的阈值(秒)。
|
*/
'waits' => [
'redis:default' => 60,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| 在这里,您可以配置希望Horizon保存最近和失败的作业的时间长度(以分钟为单位)。通常,最近
| 的作业会保留一个小时,而所有失败的作业会存储一整周。
|
*/
'trim' => [
'recent' => 10080,
'pending' => 10080,
'completed' => 10080,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Silenced Jobs
|--------------------------------------------------------------------------
|
| Silencing a job will instruct Horizon to not place the job in the list
| of completed jobs within the Horizon dashboard. This setting may be
| used to fully remove any noisy jobs from the completed jobs list.
|
*/
'silenced' => [
// App\Jobs\ExampleJob::class,
],
'silenced_tags' => [
// 'notifications',
],
/*
|--------------------------------------------------------------------------
| Metrics
|--------------------------------------------------------------------------
|
| Here you can configure how many snapshots should be kept to display in
| the metrics graph. This will get used in combination with Horizon's
| `horizon:snapshot` schedule to define how long to retain metrics.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon master
| supervisor may consume before it is terminated and restarted. For
| configuring these limits on your workers, see the next section.
|
*/
'memory_limit' => 64,
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| 在这里,您可以定义应用程序在所有环境中使用的队列辅助器设置。
| 这些管理器和设置将处理所有排队的作业,并将在部署期间由Horizon提供。
|
*/
'defaults' => [
/**
* connection:工作连接的名称
* balance=:管理器应该应用的平衡策略
* delay=0:延迟失败作业的时间
* force:强制工人在维护模式下运行
* max-processes=1:启动的最大工人数
* min-processes=1:每个队列分配的最小工人数
* memory=128:内存限制,以兆字节为单位
* nice=0:进程优先级
* paused:以暂停状态启动管理器
* queue=:工作队列的名称
* sleep=3:没有工作时休眠的秒数
* timeout=60:子进程可以运行的秒数
* tries=0:在记录任务失败之前尝试执行该任务的次数
*/
'supervisor' => [
'connection' => 'redis',
'queue' => ['default', 'event', 'send_telegram_message'],
'balance' => 'auto',//simple, auto, 和 false
'autoScalingStrategy' => 'time',//time size
'minProcesses' => 1,
'maxProcesses' => 1,
'maxTime' => 0,
'maxJobs' => 0,
'memory' => 1024,
'tries' => 3,
'timeout' => 28800,
'nice' => 0,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
'multiples' => [//可以并发的队列
'connection' => 'redis',
'queue' => ['event',],
'balance' => 'auto',//simple, auto, 和 false
'maxProcesses' => 1,
'memory' => 1024,
'tries' => 3,
'timeout' => 28800,
],
],
'environments' => [
'production' => [
'supervisor' => [
'maxProcesses' => 1,
],
'multiples' => [//可以并发的队列
'maxProcesses' => 16,
],
],
'local' => [
'supervisor' => [
'maxProcesses' => 1,
],
'multiples' => [//可以并发的队列
'maxProcesses' => 1,
],
],
'prod' => [
'supervisor' => [
'maxProcesses' => 1,
],
'multiples' => [//可以并发的队列
'maxProcesses' => 16,
],
],
'test' => [
'supervisor' => [
'nice' => 0,
],
'multiples' => [//可以并发的队列
'maxProcesses' => 16,
],
],
'*' => [
'supervisor' => [
'maxProcesses' => 1,
],
'multiples' => [//可以并发的队列
'maxProcesses' => 1,
],
],
],
];
config/octane.php
<?php
use Laravel\Octane\Contracts\OperationTerminated;
use Laravel\Octane\Events\RequestHandled;
use Laravel\Octane\Events\RequestReceived;
use Laravel\Octane\Events\RequestTerminated;
use Laravel\Octane\Events\TaskReceived;
use Laravel\Octane\Events\TaskTerminated;
use Laravel\Octane\Events\TickReceived;
use Laravel\Octane\Events\TickTerminated;
use Laravel\Octane\Events\WorkerErrorOccurred;
use Laravel\Octane\Events\WorkerStarting;
use Laravel\Octane\Events\WorkerStopping;
use Laravel\Octane\Listeners\CloseMonologHandlers;
use Laravel\Octane\Listeners\CollectGarbage;
use Laravel\Octane\Listeners\DisconnectFromDatabases;
use Laravel\Octane\Listeners\EnsureUploadedFilesAreValid;
use Laravel\Octane\Listeners\EnsureUploadedFilesCanBeMoved;
use Laravel\Octane\Listeners\FlushOnce;
use Laravel\Octane\Listeners\FlushTemporaryContainerInstances;
use Laravel\Octane\Listeners\FlushUploadedFiles;
use Laravel\Octane\Listeners\ReportException;
use Laravel\Octane\Listeners\StopWorkerIfNecessary;
use Laravel\Octane\Octane;
return [
/*
|--------------------------------------------------------------------------
| Octane Server
|--------------------------------------------------------------------------
|
| This value determines the default "server" that will be used by Octane
| when starting, restarting, or stopping your server via the CLI. You
| are free to change this to the supported server of your choosing.
|
| Supported: "roadrunner", "swoole", "frankenphp"
|
*/
'server' => env('OCTANE_SERVER', 'swoole'),
'swoole' => [
'options' => [
'log_file' => storage_path('logs/swoole_http.log'),
'package_max_length' => 10 * 1024 * 1024,
],
],
/*
|--------------------------------------------------------------------------
| Force HTTPS
|--------------------------------------------------------------------------
|
| When this configuration value is set to "true", Octane will inform the
| framework that all absolute links must be generated using the HTTPS
| protocol. Otherwise your links may be generated using plain HTTP.
|
*/
'https' => env('OCTANE_HTTPS', false),
/*
|--------------------------------------------------------------------------
| Octane Listeners
|--------------------------------------------------------------------------
|
| All of the event listeners for Octane's events are defined below. These
| listeners are responsible for resetting your application's state for
| the next request. You may even add your own listeners to the list.
|
*/
'listeners' => [
WorkerStarting::class => [
EnsureUploadedFilesAreValid::class,
EnsureUploadedFilesCanBeMoved::class,
],
RequestReceived::class => [
...Octane::prepareApplicationForNextOperation(),
...Octane::prepareApplicationForNextRequest(),
// 开启对 Dcat Admin 的支持
Dcat\Admin\Octane\Listeners\FlushAdminState::class,
],
RequestHandled::class => [
//
],
RequestTerminated::class => [
// FlushUploadedFiles::class,
],
TaskReceived::class => [
...Octane::prepareApplicationForNextOperation(),
//
],
TaskTerminated::class => [
//
],
TickReceived::class => [
...Octane::prepareApplicationForNextOperation(),
//
],
TickTerminated::class => [
//
],
OperationTerminated::class => [
FlushOnce::class,
FlushTemporaryContainerInstances::class,
// DisconnectFromDatabases::class,
// CollectGarbage::class,
],
WorkerErrorOccurred::class => [
ReportException::class,
StopWorkerIfNecessary::class,
],
WorkerStopping::class => [
CloseMonologHandlers::class,
],
],
/*
|--------------------------------------------------------------------------
| Warm / Flush Bindings
|--------------------------------------------------------------------------
|
| The bindings listed below will either be pre-warmed when a worker boots
| or they will be flushed before every new request. Flushing a binding
| will force the container to resolve that binding again when asked.
|
*/
'warm' => [
...Octane::defaultServicesToWarm(),
],
'flush' => [
//
],
/*
|--------------------------------------------------------------------------
| Octane Swoole Tables
|--------------------------------------------------------------------------
|
| While using Swoole, you may define additional tables as required by the
| application. These tables can be used to store data that needs to be
| quickly accessed by other workers on the particular Swoole server.
|
*/
'tables' => [
'example:1000' => [
'name' => 'string:1000',
'votes' => 'int',
],
],
/*
|--------------------------------------------------------------------------
| Octane Swoole Cache Table
|--------------------------------------------------------------------------
|
| While using Swoole, you may leverage the Octane cache, which is powered
| by a Swoole table. You may set the maximum number of rows as well as
| the number of bytes per row using the configuration options below.
|
*/
'cache' => [
'rows' => 1000,
'bytes' => 10000,
],
/*
|--------------------------------------------------------------------------
| File Watching
|--------------------------------------------------------------------------
|
| The following list of files and directories will be watched when using
| the --watch option offered by Octane. If any of the directories and
| files are changed, Octane will automatically reload your workers.
|
*/
'watch' => [
'app',
'bootstrap',
'config/**/*.php',
'database/**/*.php',
'public/**/*.php',
'resources/**/*.php',
'routes',
'composer.lock',
'.env',
],
/*
|--------------------------------------------------------------------------
| Garbage Collection Threshold
|--------------------------------------------------------------------------
|
| When executing long-lived PHP scripts such as Octane, memory can build
| up before being cleared by PHP. You can force Octane to run garbage
| collection if your application consumes this amount of megabytes.
|
*/
'garbage' => 50,
/*
|--------------------------------------------------------------------------
| Maximum Execution Time
|--------------------------------------------------------------------------
|
| The following setting configures the maximum execution time for requests
| being handled by Octane. You may set this value to 0 to indicate that
| there isn't a specific time limit on Octane request execution time.
|
*/
'max_execution_time' => 30,
];
启动效果如下

最后,线上部署的时候用Supervisor守护一下即可。
[program:e-workers]
command=/www/server/php/82/bin/php artisan workers restart
directory=/www/wwwroot/laravel12/
autorestart=true
startsecs=3
startretries=3
stdout_logfile=/www/server/panel/plugin/supervisor/log/e-workers.out.log
stderr_logfile=/www/server/panel/plugin/supervisor/log/e-workers.err.log
stdout_logfile_maxbytes=2MB
stderr_logfile_maxbytes=2MB
user=www
priority=999
numprocs=1
process_name=%(program_name)s_%(process_num)02d
本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu
推荐文章: