队列、定时任务与推送
2026/10/26大约 2 分钟
队列、定时任务与推送
L4 链路扩展:把耗时动作丢进队列、把周期动作交给定时任务、把实时消息推给在线客户端。三者都由基座统一调度,插件只在 boot() 声明、不写消费者/不起独立进程。
队列(QueueService)
webman/redis-queue 的消费者类扫描目录写死为 app/queue/redis,插件目录的消费者不会被扫。因此采用单一信封队列 plugin_job:基座只提供一个消费者 PluginJobConsumer,所有插件任务投到 plugin_job,消息体携带 handler 名,消费时按名转发到注册的回调。
插件永远不写消费者类,无需重启即可新增 handler。
use app\service\QueueService;
// ① 投递(handler 命名约定 {plugin}_{action})
QueueService::dispatch(
'am_demo_order_mail',
['order_id' => 1001],
delay: 0,
dedupKey: '' // 可选幂等去重键
);
// ② 注册处理回调(写在 boot())
QueueService::consume(
'am_demo_order_mail',
fn(array $payload): void => MailService::sendOrderMail($payload['order_id']),
owner: 'am_demo'
);- 回调签名
fn(array $payload): void,须幂等(队列可能重投)。 payload必须可json_encode。- 队列不可用时
dispatch仅记日志返回false,不抛异常、不阻断主流程(开关queue_enabled)。
定时任务(TaskRegistry)
use app\basic\TaskRegistry;
// 写在 boot()
TaskRegistry::schedule(
'am_demo_clean', // 任务名,建议 {plugin}_{action} 避免跨插件冲突
'0 3 * * *', // 标准 5 段 cron
fn() => CleanService::run(), // 任务体:无参、须幂等
owner: 'am_demo'
);由自定义进程 app\process\PluginTask(count=1)在 onWorkerStart 时把清单注册到 workerman/crontab。任务体自带 Redis 锁防多进程重复执行(单进程调度 + Redis SETNX 双保险)。开关 task_enabled。
推送(PushService)
封装 webman/push(Pusher 协议)。失败仅记日志返回 false,不抛异常。
use app\service\PushService;
// 推给某个 sid 的私有频道(private-sid_{sid})
PushService::toUser($sid, 'order.created', ['order_id' => 1001]);
// 推给任意频道(公共频道直接命名;私有频道须 private- 前缀)
PushService::toChannel('room-1024', 'chat.msg', ['text' => 'hi']);push 已作为通道注册进通知中心,可直接用 NotifyService 模板发送到 push。开关 push_enabled。
私有频道鉴权(ChannelAuthRegistry)
私有频道订阅时由 app\basic\ChannelAuthRegistry 裁决当前 sid 是否可监听。默认策略:private-sid_{sid} 仅本人可订阅,其余频道放行。插件可注册更细的鉴权器:
use app\basic\ChannelAuthRegistry;
// 写在 boot()
ChannelAuthRegistry::authorize(
'private-am_im_room_*', // fnmatch 通配模式
fn(string $sid, string $channel): ?bool => RoomService::isMember($sid, $channel),
owner: 'am_demo'
);- 回调返回
true允许 /false拒绝 /null交下一个(或走默认策略)。 - 多模式命中按注册先后,首个非
null裁决。
进程级
以上四类注册一律写在 boot(),传 owner=插件名,禁用/卸载时基座按 owner 对称清理。详见生命周期。
