Search by

goletter / hyperf-server

goletter

A api server component for hyperf.(based https://github.com/goletter/hyperf-server)

Package info

github.com/goletter/hyperf-server

pkg:composer/goletter/hyperf-server

Statistics

Installs: 621

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.0.8 2026-09-10 18:27 UTC

README

Hyperf API 服务基础组件:路由、中间件、异步队列封装(含任务链)、模型 Cast 等。

安装

composer require goletter/hyperf-server

功能概览

模块 说明
Router 扩展 Hyperf Router,提供 apiResource
Middleware CORS、Trace、Locale、幂等、响应格式化、模型绑定等
QueueService 异步队列推送 / 批量 / 任务链 / 按 key 串行,自动透传 trace_id
Casts EncryptedJsonArrayCastResourceUrl
MakeModuleCommand 模块脚手架命令

队列 QueueService

依赖项目已配置 hyperf/async-queue(可选配合 goletter/hyperf-queue 的毫秒池)。

use Goletter\Server\Service\QueueService;
use Hyperf\Context\ApplicationContext;

/** @var QueueService $queue */
$queue = ApplicationContext::getContainer()->get(QueueService::class);

推送 / 延迟 / 批量

// 立即推送
$queue->push(new SendNotifyJob($id));

// 延迟推送(delay 单位与驱动一致:default 一般为秒,ms 池为毫秒)
$queue->delay(new SendNotifyJob($id), 5);
$queue->push(new SendNotifyJob($id), 'ms', 200);

// 批量并行入队(可被多个 worker 同时消费)
$queue->pushBatch([
    new JobA($id),
    new JobB($id),
]);

当 Context 中存在 trace_id 时,push 会自动包装为 TraceableJob,在 handle() 内恢复链路上下文。

任务链 chain

按顺序执行:上一步成功后才入队下一步;任一步抛异常则中断后续

$queue->chain([
    new CreateOrderJob($dto),
    new DeductStockJob($dto),
    new SendNotifyJob($dto),
]);

// 步与步之间延迟(default 池:秒)
$queue->chain([
    new Step1Job($id),
    new Step2Job($id),
], QueueService::QUEUE_DEFAULT, 2);

// ms 池:步间 200ms(需配置 RedisMsDriver)
$queue->chain([
    new Step1Job($id),
    new Step2Job($id),
], 'ms', 200);

实现要点:

  • 入口只入队一个 ChainJob;当前步在同一次消费中执行,成功后再 push 剩余链
  • pushBatch 不同:chain 串行,pushBatch 并行
  • ChainJob 本身不重试(maxAttempts = 0),避免失败重试导致链被重复推进
  • 链内 Job 须可序列化(勿放入 Container、闭包、PDO 等)

按 key 串行 pushSerial(陆续入队)

适合任务一个个进来、事先不知道完整列表的场景。同一 key 下 FIFO 串行;不同 key 互不阻塞。

// 用户 A 陆续提交 —— 按提交顺序执行
$queue->pushSerial('user:1001', new ProcessTaskJob($task1));
$queue->pushSerial('user:1001', new ProcessTaskJob($task2));

// 间隔 5 秒;失败最多共执行 3 次(含首次),未耗尽则按 delay 重试同一条
$queue->pushSerial('user:1001', new ProcessTaskJob($task3), 'default', 5, 3);

// ms 池:步间 / 重试间隔 200ms
$queue->pushSerial('user:1001', new ProcessTaskJob($task4), 'ms', 200, 3);

// 用户 B 不受 A 阻塞
$queue->pushSerial('user:1002', new ProcessTaskJob($taskX));

$queue->serialWaitingCount('user:1001');
API 何时用
chain([...]) 步骤已知,一次性投递;失败中断后续
pushSerial($key, $job, $queue, $delay, $maxAttempts) 陆续入队;同 key FIFO;失败按 delay 重试
pushBatch([...]) 全部并行,无顺序

实现要点:

  • 任务写入 Redis List {queue-serial}:{key}:waiting;列表从空变为 1 时启动 runner(首个立即执行)
  • $delay:成功后到下一条的间隔;失败且未达上限时,也是重试同一条的间隔
  • $maxAttempts:本条最多执行次数(默认 1 = 失败不重试,直接丢弃并继续下一条)
  • 重试耗尽后丢弃本条并继续后续,避免堵死同 key
  • 外层 SerialJob 不依赖 async-queue 重试(避免双调度)
  • 全局并发:默认最多 32 个 SerialJob 同时跑(跨 key);满了会 busy_delay 后再抢槽,减轻「Too many open files」
php bin/hyperf.php vendor:publish goletter/hyperf-server

config/autoload/queue_serial.php

return [
    'max_concurrent' => 32,   // 0 = 不限制
    'busy_delay' => 1,
    'slot_lease_seconds' => 600,
];

数据量大时建议同时:调低 async_queue.*.concurrent.limit、提高进程 ulimit -n、日志尽量打 stdout/stderr(避免每条日志抢文件句柄)。

队列是否空闲

$status = $queue->getAsyncQueueCompleted('default');
// ['queue' => 'default', 'completed' => bool, 'working' => int, 'backlog' => int, 'failed' => int]

会统计 waitingdelayed 以及 Redis reserved(官方 info() 不含 reserved)。

路由 apiResource

use Goletter\Server\Router\Router;

Router::apiResource('users', App\Controller\UserController::class);
// GET/POST /users
// GET/PUT|PATCH/DELETE /users/{user}

License

MIT