dleno / common-core
common core
Requires
- php: >=8.4
- ext-curl: *
- ext-libxml: *
- ext-mbstring: *
- ext-openssl: *
- ext-pdo: *
- ext-simplexml: *
- ext-swoole: >=6.2
- ext-xml: *
- dleno/dingtalk: ~3.2.0
- dleno/hyperf-env-multi: ~3.2.0
- hashids/hashids: ^4.1
- hyperf/amqp: ~3.2.0
- hyperf/async-queue: ~3.2.0
- hyperf/cache: ~3.2.0
- hyperf/codec: ~3.2.0
- hyperf/collection: ~3.2.0
- hyperf/command: ~3.2.0
- hyperf/config: ~3.2.0
- hyperf/context: ~3.2.0
- hyperf/contract: ~3.2.0
- hyperf/coordinator: ~3.2.0
- hyperf/coroutine: ~3.2.0
- hyperf/crontab: ~3.2.0
- hyperf/database: ~3.2.0
- hyperf/db-connection: ~3.2.0
- hyperf/di: ~3.2.0
- hyperf/engine: ^2.15
- hyperf/event: ~3.2.0
- hyperf/exception-handler: ~3.2.0
- hyperf/framework: ~3.2.0
- hyperf/guzzle: ~3.2.0
- hyperf/http-message: ~3.2.0
- hyperf/http-server: ~3.2.0
- hyperf/json-rpc: ~3.2.0
- hyperf/logger: ~3.2.0
- hyperf/memory: ~3.2.0
- hyperf/paginator: ~3.2.0
- hyperf/pool: ~3.2.0
- hyperf/process: ~3.2.0
- hyperf/redis: ~3.2.0
- hyperf/rpc-client: ~3.2.0
- hyperf/serializer: ~3.2.0
- hyperf/server: ~3.2.0
- hyperf/signal: ~3.2.0
- hyperf/snowflake: ~3.2.0
- hyperf/stringable: ~3.2.0
- hyperf/support: ~3.2.0
- hyperf/validation: ~3.2.0
- hyperf/websocket-server: ~3.2.0
- jenssegers/optimus: ^1.1
- monolog/monolog: ^3.10
- nikic/fast-route: ^1.3
- php-amqplib/php-amqplib: ^3.5
- psr/container: ^1.0 || ^2.0
- psr/http-message: ^1.0 || ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- psr/log: ^2.0 || ^3.0
- swow/psr7-plus: ^1.0
- symfony/property-access: ^6.4 || ^7.0
- symfony/serializer: ^6.4 || ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^1.12
- swoole/ide-helper: ^6.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-25 10:15:54 UTC
README
dleno/common-core 是 Dleno Hyperf 3.2 项目的公共核心包,负责沉淀 HTTP/RPC/Model/AMQP/AsyncQueue/WebSocket 等基础能力,以及项目通用工具、全局函数、异常输出、日志输出、分布式锁和示例模板。
它的定位是“框架基座 + 业务可接入的公共组件”,不是一个完整业务应用。业务项目只需要安装包、补齐少量 DI 绑定和业务策略,就可以复用统一的请求解析、输出格式、模型能力、队列封装、WebSocket 连接管理和在线检查能力。
环境要求
- PHP
>= 8.4 - Swoole
>= 6.2 - Hyperf
~3.2 - ext-curl
- WebSocket 功能依赖 Redis
7.4+的HEXPIRE能力
启用 WebSocket 服务时,启动前会校验:
server.mode必须是SWOOLE_BASE,否则直接终止启动。- Redis 可达但不支持
HEXPIRE时直接终止启动。 - Redis 暂时不可达时只输出 WARN 放行,避免启动编排中的短暂网络抖动误杀服务;运行时仍要求 Redis 7.4+ 可用。
安装
composer require dleno/common-core:~3.2.0
安装后包内 ConfigProvider 会自动生效:
- 扫描
src/下的注解。 - 自动加载
src/common/Functions.php全局函数。 - 通过
dleno/hyperf-env-multi显式加载环境文件:先加载.env,再按APP_ENV加载.env.<APP_ENV>,环境文件中的同名变量以后者为准。 - 注入部分基础 dependencies。
- 按
ENABLE_HTTP/ENABLE_WS自动注入 HTTP / WS 基础中间件。 - 提供 WebSocket 配置发布模板,可通过
vendor:publish写入config/autoload/websocket.php。
发布配置:
php bin/hyperf.php vendor:publish dleno/common-core
从 3.1 升级
3.2 版本跟随 Hyperf 3.2,要求 PHP >= 8.4、Swoole >= 6.2;dleno/hyperf-env-multi、dleno/dingtalk 需同步到 ~3.2.0。业务项目需要跟着调整:
-
config/autoload/logger.php:LoggerConfig::getDefaultConfig()改为返回 Hyperf 3.2 的['default' => ..., 'channels' => [...]]结构。自定义分组以参数传入:return \Dleno\CommonCore\Logger\LoggerConfig::getDefaultConfig([ 'custom' => ['handlers' => [\Dleno\CommonCore\Logger\LoggerConfig::fileHandler('custom-info.log', \Monolog\Level::Info)]], ]);
旧写法
array_merge(LoggerConfig::getDefaultConfig(), [...])会把自定义分组放到channels之外,静默失效。 -
config/autoload/cache.php:改为['default' => 'default', 'stores' => ['default' => [...]]]。 -
自定义 AsyncQueue Job 如覆盖了
getQueueName(),改为getPoolName()。BaseJob::getPoolName()返回$queue并按需注册动态队列,因此Hyperf\AsyncQueue\dispatch($job)与AsyncQueue::push($job)落到同一队列。 -
框架
DriverFactory现绑定为包内BaseDriverFactory同一实例:dispatch()、#[AsyncQueueMessage]、queue:*命令和框架ConsumerProcess统一使用带 Redis hash tag 的 channel(如{queue}:waiting)。3.1 时经这些框架入口写入未加 tag 的 channel(如queue:waiting)的存量消息,升级前请消费完或自行迁移。 -
async_queue.*不要设置 3.2 配置模板里的enable:设为 true 时框架会为该池自动再起一个queue.<pool>消费进程,与BaseQueueConsumer重复消费同一队列,且不受isEnable/local 开关约束。 -
继承 Hyperf
ConsumerProcess的消费进程在 3.2 只认$pool,只写$queue会静默改为消费 default 池;动态队列请继承BaseQueueConsumer。 -
框架
dispatch($job)在 3.2 投递到 Job 的$queue(3.1 固定进 default 池,未绑定 DriverFactory 的项目会写入无人消费的queue:*);若用过dispatch()/#[AsyncQueueMessage],升级前清点无 tag 的queue:waiting/delayed/reserved/timeout/failed。 -
包内
ResumeExitCoordinatorListener已删除(Hyperf 3.2 同步移除;命令结束时由框架Command自行唤醒退出协调器)。 -
测试基于 PHPUnit 11:
Hyperf\Testing\TestCase每个用例都会重建容器并再次注册 error handler,PHPUnit 会报 risky,可参考dleno/hyperf-diy的test/TestCase.php在 tearDown 中还原。
接入清单
HTTP 请求对象绑定
Hyperf 自身也会绑定 RequestParserInterface 和 RequestInterface,因此这两个绑定需要放在业务项目 config/autoload/dependencies.php,确保优先级稳定:
return [ Hyperf\HttpMessage\Server\RequestParserInterface::class => Dleno\CommonCore\Core\Request\RequestParser::class, Hyperf\HttpServer\Contract\RequestInterface::class => Dleno\CommonCore\Core\Request\Request::class, ];
CoreMiddleware 和 DispatcherFactory 已由 common-core 的 ConfigProvider 注入,通常不需要业务项目重复绑定。
HTTP 模块前置中间件绑定
common-core 会在 HTTP InitMiddleware 之后自动注册 AbstractModuleBeforeMiddleware,并默认绑定到包内 DefaultModuleBeforeMiddleware。默认实现会按 config('app.api_check_sign') / config('app.api_data_crypt') 执行签名校验和请求解密,checkAuth() 为 no-op,checkReplay() 返回 true 放行。
业务需要登录校验、防重放或自定义签名参数时,推荐继承 DefaultModuleBeforeMiddleware,只覆写需要的钩子,并在业务项目 config/autoload/dependencies.php 中覆盖抽象基类绑定:
return [ Dleno\CommonCore\Middleware\Http\AbstractModuleBeforeMiddleware::class => App\Middleware\AppModuleBeforeMiddleware::class, ];
如未覆盖该绑定,会使用包内默认实现:签名/解密仍按配置开关执行,但不会做业务登录校验或防重放拦截。
参考实现见:
examples/Middleware/AppModuleBeforeMiddleware.php
异常处理器配置
异常处理链顺序敏感,DefaultExceptionHandler 会兜底并终止后续传播。因此 common-core 不通过 ConfigProvider 自动注入 exceptions.handler,而是提供 ExceptionHandlerConfig 让业务项目在 config/autoload/exceptions.php 中显式生成默认链,并保留业务 handler 的插入位置:
use Dleno\CommonCore\Exception\ExceptionHandlerConfig; return [ 'handler' => ExceptionHandlerConfig::defaultHandlers( httpCommonHandlers: [ App\Exception\Handler\BusinessCommonExceptionHandler::class, ], wsCommonHandlers: [ App\WebSocket\Exception\Handler\BusinessCommonWsExceptionHandler::class, ], httpBeforeDefault: [ App\Exception\Handler\BusinessOutputExceptionHandler::class, ], wsBeforeDefault: [ App\WebSocket\Exception\Handler\BusinessWsOutputExceptionHandler::class, ], ), ];
httpCommonHandlers / wsCommonHandlers 会插入到 common-core 的 CommonExceptionHandler 之后,用于回滚、审计、上下文清理等公共前置处理,不应调用 stopPropagation();httpBeforeDefault / wsBeforeDefault 会插入到对应协议的 DefaultExceptionHandler 之前,用于业务输出类 handler。未启用 ENABLE_HTTP / ENABLE_WS 时不会生成对应 server 的异常链。
WebSocket 业务策略绑定
WebSocket 的连接维度属于业务决策,common-core 不提供默认绑定策略。业务项目必须在 dependencies.php 中绑定 WsBindStrategyInterface:
return [ Dleno\CommonCore\Websocket\Contract\WsBindStrategyInterface::class => App\WebSocket\Bind\DefaultWsBindStrategy::class, ];
WsHookInterface 已有 no-op 默认实现,业务不需要身份解析、握手校验或生命周期钩子时可以不覆盖;需要接入登录态、握手鉴权、open/close/heartbeat/message/send 等业务逻辑时再绑定:
return [ Dleno\CommonCore\Websocket\Contract\WsHookInterface::class => App\WebSocket\Hook\AppWsHook::class, ];
参考实现见:
examples/WebSocket/Bind/DefaultWsBindStrategy.phpexamples/WebSocket/Bind/MultiDimWsBindStrategy.phpexamples/WebSocket/Hook/AppWsHook.php
核心能力
HTTP 基座
相关目录:
src/Base/BaseCoreController.phpsrc/Middleware/Httpsrc/Core/Requestsrc/Core/Routesrc/Annotationsrc/Aspect
主要能力:
- Controller 自动注入对应 Service。
successData()统一返回结构。checkParams()参数校验。LockCheckTrait提供分布式锁辅助。OutputHtml、OutputNoLog、OutputNotFormat等注解控制输出格式、日志和字段格式化。- HTTP 输出切面自动记录响应日志,并在
API_DATA_CRYPT开启时加密响应体。 - HTTP 请求初始化、语言/时区/traceId 等上下文处理。
- 路由分发增强和请求对象替换。
- AutoController 请求方式控制:方法级
#[AllowMethods]优先,其次类级AutoController(defaultMethods)、config('app.default_allow_methods')、默认['POST', 'GET'];包含GET时自动补HEAD,OPTIONS预检由InitMiddleware统一处理。 - 模块前置中间件提供签名校验、请求解密、登录校验和防重放钩子。
AbstractModuleBeforeMiddleware封装流程,DefaultModuleBeforeMiddleware提供默认钩子实现,业务通常继承默认实现后在dependencies.php绑定抽象基类来接管。该中间件只处理已命中路由(FOUND)且处理器为注解控制器(#[AutoController]/#[Controller])的请求,config/routes.php里的闭包路由(健康检查、favicon 等)不做签名/解密/登录校验;NOT_FOUND/METHOD_NOT_ALLOWED会放行给管线末端CoreMiddleware返回 404 / 405,避免未知路由被误判为签名错误。请求解密后的参数会随请求向下传递,控制器可直接取用;解密失败返回 400。
示例:
examples/Httpexamples/Middlewareexamples/Config
异常与输出
相关目录:
src/Exceptionsrc/Tools/OutPut.phpsrc/Tools/Outputsrc/Tools/Logger.php
主要能力:
- HTTP/RPC 常用异常处理器。
ExceptionHandlerConfig生成 HTTP / WS 默认异常链,并支持业务公共前置 handler、兜底前输出 handler 插入到固定位置。- 统一 JSON 输出。
- API/RPC/Error 输出日志封装。
- HTTP/WS Controller 响应日志切面。
- 按渠道输出 stdout、system、api、sql、business 日志。
LogFileHandler写日志(建目录、打开、写入、轮转清理)全程不改动进程级错误处理器栈:Swoole 文件 hook 下写日志让出协程时,其他协程的 warning 仍交给 Hyperf 的 ErrorExceptionHandler,不会被 monolog 临时处理器吞掉;写入失败(含句柄被其他协程轮转关闭)时关闭重开,最多重试 3 次。- 错误输出可读取业务
config/autoload/dingtalk.php的dingtalk.trace配置发送异常追踪通知。
Model 与数据库
相关目录:
src/Modelsrc/Db
主要能力:
BaseModel统一模型基类。- 自定义
EloquentBuilder,支持分页、分组统计、批量插入更新、全文检索等项目增强方法。 - 支持按年/月/日/周/固定数量分表。
findById()、updateById()、deleteById()按主键路由到对应分表。withTable()支持指定分表后缀查询。- 模型生成时保持 decimal 精度处理。
示例:
examples/Model/BaseModel.phpexamples/Model/Test.phpexamples/Model/TestSplit.php
AMQP
相关目录:
src/Base/Amqpsrc/Tools/Amqp
主要能力:
BaseProducer、BaseConsumer封装 Hyperf AMQP。- 支持延迟交换机。
- 支持死信交换机、死信路由、消息 TTL、队列 TTL。
- 支持生产者确认。
- 支持静态队列和按服务器动态 routingKey / queue。
示例:
examples/Amqpexamples/Amqp/Dynamic
AsyncQueue
相关目录:
src/Base/AsyncQueuesrc/Tools/AsyncQueue
主要能力:
BaseJob统一队列 Job 基类。BaseQueueConsumer统一消费进程基类。BaseDriverFactory支持动态队列驱动配置。AsyncQueue::push()会按 Job 的 queue 自动选择对应 driver。- 支持静态队列和按服务器动态队列。
示例:
examples/AsyncQueueexamples/AsyncQueue/Dynamic
v3.2.5 升级说明
- 修复应用继承包内 AMQP ConsumerFactory 时的自身委托,保留应用覆盖及 make 参数透传。
- Redis 收尾按每次 consume 隔离准备中和运行中的回调,保留在途处理、ACK/retry 和直接 defer。
- 仅为已有包内 cron 任务延后原生 worker 退出清理,保留锁续期和连接资源;没有任务时沿用父流程,不新增 HTTP Worker 信号。
- 配置兼容性变化:包内 AMQP 工厂拒绝心跳0、负数及非整数;省略或null默认15秒。读写超时缺失或null默认30秒,显式值保留。环境变量空字符串经配置帮助类转成0也会报错,请在升级前修正。
- 本版没有新增强制退出、提前确认、业务请求重发或重试机制。持续MQ不可达时的框架关闭限制仍存在,详见下节及隔离回归记录。
- 消费者应先正常 Composer 更新并提交 lock,核对实际加载版本和类,再清理临时覆盖及旧代理缓存;保留应用业务恢复逻辑。不要手改 vendor。
进程停止与容器宽限
v3.2.5 要求 AMQP 使用正整数心跳,缺失时默认心跳15秒、读写超时30秒。升级前检查原有零心跳配置及应用临时退出覆盖。协程资源耗尽时的收尾属于已知边界,本版本未扩展处理;退出宽限应按实际任务和依赖故障配置。
ProcessStopHandler 仅在 PROCESS 范围注册 SIGTERM / SIGINT,只设置 ProcessManager::setRunning(false)。WORKER_EXIT 表示资源清理:AMQP 连接沿用框架正心跳协程关闭逻辑;它还会清空框架 Memory 缓存、释放 server mutex 并停止 Timer 续期,不能在通用停止信号到达时提前触发。正常流程是先结束消费并等待在途回调完成确认及 finally/defer,再由 Hyperf AbstractProcess 清理 Timer 和唤醒 WORKER_EXIT。不额外接管 HTTP Worker,不强制取消业务协程、不提前 ACK、不因停止信号重发业务请求。
默认 RedisDriver 通过 dependencies 解析为包内子类,仅包装框架回调并在 consume() 外等待在途回调协程结束(包含业务 defer);默认 AMQP Consumer 通过框架 PriorityDefinition 接入包内 factory/子类,将约五秒的并发收尾限制改为等待回调协程完成,包含业务 defer。两者都不重写消费/确认/异常恢复/重试逻辑,不设内部退出硬上限,也覆盖框架因消息数量上限触发的正常收尾。BaseConsumer::waitTimeout=1 让 AMQP 空闲 channel 定期检查停止标记;这是消费等待检查间隔,不是心跳、业务处理超时或重试间隔,业务子类显式设置仍优先。
Redis 收尾按每次 consume() 的协程单独计数,从回调准备好、尚在等待并发槽位或子协程启动时就纳入 pending,直到该回调协程的 defer 完成才移出。同一个 driver 的其他 consume 不阻塞本次正常收尾;进程级资源清理仍需等待进程自身 handle 管理的所有 consume。AMQP factory 继续支持继承包内 Factory 的应用覆盖及参数透传,委托到应用 factory 后不再委托给其自身。 自定义 Redis getCallback 装饰器必须执行父类返回的回调;若在调用前抛错或丢弃该回调,pending 无法结束,仍可能等到容器硬上限。协程创建失败、强制取消及无限阻塞也不属于成功收尾保证,应用仍需设置停止宽限。
这里的 defer 保证针对直接任务/回调协程。嵌套 parallel() 的 WaitGroup 会在子协程 defer 前完成,parallel() 返回不代表子协程 defer 已结束;未被直接任务显式等待的子协程和后台工作不在此计数内。若这些收尾还需 HTTP、AMQP 或锁,应在子工作中用 try/finally 完成必要 IO,或让任务等待一个在全部必要 defer 完成后才发送的完成信号,再返回给框架确认。不要仅凭 parallel() 返回判定所有子协程已经收尾。本修复不递归追踪任意子协程,也不改变框架 ACK 时机。
默认 AMQP ConnectionFactory 继续调用父工厂构造连接,只在建连前解析和校验 params.heartbeat:省略或 null 默认 15 秒;显式 0、负数或非整数抛 InvalidArgumentException,在 IO 构造和网络连接之前失败;合法正整数保持不变。该规则同样适用于 AMQP_HEARTBEAT 经配置帮助类生成的参数,原来显式配置 0 的应用需要先改配置;AmqpDbConfig 会把空字符串环境变量转成0,因此空的 AMQP_HEARTBEAT 也应删除或改为正整数。这是与旧配置的兼容性变化,不会静默把 0 改为默认值。
包内 AmqpDbConfig 原有默认心跳15秒、读写超时30秒继续保留;工厂也采用相同的15/30秒默认组合。read_write_timeout 省略或 null 时补30秒,已有显式值保持不变;IO、连接池和其他参数不改。配置自定义心跳时,建议配套设置 read_write_timeout(框架模板为心跳的两倍);自定义 IO 应遵循其要求,例如 php-amqplib StreamIO 要求至少两倍、SocketIO 要求大于两倍(15秒心跳时需显式配置大于30秒)。省略读写超时的配置现从框架原生3秒改为30秒;对端失联、发布阻塞或退出时 broker 不可达的等待也可能变长,应用应据此配置停止宽限,显式超时不被覆盖。长时间同步阻塞事件循环会阻止心跳发送,业务应选择合适的正数心跳及配套超时,保持协程可调度。
不再添加包内逐连接 WORKER_EXIT watcher、PID 标记或访问框架 protected 退出状态;全部沿用框架正心跳生命周期。HTTP/task worker 和命令也沿用这一框架行为,不额外注册信号或接管退出;WORKER_EXIT 后的后台协程不能假定 AMQP 连接仍可发布。已获准的 cron 延后清理仍会先等已有任务及直接defer完成,再触发资源清理。应用显式覆盖 ConnectionFactory 仍优先;继承包内工厂并调用 parent::make 保留校验,完全改用自有/原生工厂会绕过该规则,需要应用自行遵守正心跳约束。这里不保证框架正心跳协程会在每次连接 close 后立即回收;其原有资源行为未扩展修改。
实际故障回归确认:MQ正常时,重复停止和worker轮换能够先完成任务。MQ持续不响应或网络断连时,当前Hyperf关闭AMQP连接可能等不到关闭回复,随后由Swoole死锁检测结束;退出码0不代表完整关闭,读写超时30秒也不是关闭硬上限。该限制只涉及实际使用AMQP连接的故障退出,本轮未新增强制关闭或重试机制。测试中永久故障是在业务确认后注入,确认前后broker回读均为空;不能据此保证失联期间未确认的业务也绝不重投。
BaseQueueConsumer 和包内 Process\CrontabDispatcherProcess 的 recvTimeout=1.0 仅缩短继承自 Hyperf AbstractProcess 的内部消息管道读取等待,不是 Redis 取消息超时、HTTP 请求超时或任务 handle_timeout。原 reloadChannels 启动恢复、reloadCount、队列名和 hash tag、retry_seconds、框架五秒重启间隔保持不变。
调度子类保留框架的分钟等待、去重、任务分发和 getInterval()。仅在独立 Swoole PROCESS 且策略精确为框架 WorkerStrategy、TaskWorkerStrategy 或 ProcessStrategy 时,增加每秒检查 running 的协程,停止后定向唤醒本进程协调器。这三种策略将业务发到其他 worker 执行;策略子类不在白名单。检查协程在父 handle 异常后的框架清理阶段也会结束,避免阻止进程重启。
CoroutineStrategy 和应用自定义策略不走提前唤醒,继续按框架分钟边界退出。包内 CrontabExecutor 在任何 server/task 锁 IO 之前计入已开始任务,在任务协程所有 finally/defer 完成后移出;调度进程交回 AbstractProcess 清理之前等待计数归零。这样也保护框架在停止后的分钟边界分发出的那一批任务。未到点任务若在等待期间按原时机开始,会一起收尾;清理时仍未开始的任务由框架关闭,不提前执行、不重发。该模式的停止宽限需包含等待分钟边界、已开始任务和五秒重启间隔。
上述计数只覆盖经过默认包内 Executor 的工作;应用覆盖 Executor 或在监听器、AOP、自定义策略中另起未等待的后台协程,需独立评估。默认跨 worker 策略的快速唤醒也会先等本进程已计入的 Executor 任务完成。接收 worker 若已有包内 Executor 任务,包内 WorkerExitCallback 延后父回调的 OnWorkerExit 事件及 WORKER_EXIT 资源清理,直到直接任务及其 finally/defer 完成,以保留锁续期、Memory、HTTP 和 AMQP 连接;延后期间尚未开始的定时任务直接结束,不执行或重发;此分支不打印执行失败日志,而随后由框架资源清理关闭的未到点任务仍沿用框架 failed execution 日志,排障时需区分。仅在这个分支增加 10ms 保活定时器,父回调仍在原生非协程上下文运行,之后保留框架可重复回调的语义。没有在途 cron 时直接走父实现,不增加 HTTP Worker 信号、不改变任务分发或请求处理。保活定时器创建失败会记录错误并回退框架原行为,此时不能保证延后资源清理。应用覆盖该退出回调或 Executor 时需自行保留对应收尾能力。接收 worker 仍受应用的 Swoole max_wait_time 约束;充足的容器停止宽限不能替代该 worker 上限,本包不改其默认值。本次验证的 Swoole 6.2.3 在未配置 max_wait_time 时底层默认三秒;应用或 server.php 已设置该项时以应用配置为准。共享进程的协程服务器模式不提前唤醒,本次没有用真实共享服务器验证它的完整退出流程;不能据此承诺统一十秒退出。
ENABLE_CRONTAB 开启时仍只注册框架 Hyperf\Crontab\Process\CrontabDispatcherProcess 标识,dependencies 默认解析为包内子类;关闭开关不注册调度进程。应用自身 dependencies 仍可覆盖调度、Executor、WorkerExitCallback、ConnectionFactory、RedisDriver 和 AMQP Consumer;既有 AMQP ConsumerFactory 覆盖及 make() 参数也会保留。自定义 driver、覆盖后的 Consumer 或显式无限 AMQP waitTimeout 不自动获得上述保证。不要重复注册进程或停止处理器。包内不定义 signal.timeout,配置合并方式不变。
本修复不保证所有任务十秒内结束。容器停止宽限应包含最长剩余任务时间、框架五秒重启间隔及调度开销。例如七秒任务刚开始就停止,安全收尾可能需要约十二秒;任务执行三秒后停止,剩余约四秒加五秒间隔才有机会在十秒内退出。十二秒任务配三十秒宽限可完整完成,十秒硬上限可能被 SIGKILL、消息尚未确认。不得通过提前确认、清队列或降低业务超时加速退出。
空闲 BaseConsumer 每秒会产生一次框架 WaitTimeout 事件,应用对此事件的日志/告警监听器需相应评估。AMQP 的确认仍由框架在回调完成后发送;停止接收后等待在途期间,broker 预取但尚未开始执行的消息可在连接关闭后返回 ready,这不代表业务重复执行。Redis 已出队但正在等待并发槽位的消息也要按原流程完成;handle_timeout 的原有到期恢复语义不变,仍需合理配置。WS 消息/控制队列继承 Redis drain 和管道等待修正;WS 注册进程原有 15 秒 sleep 周期不变。未被回调等待的应用后台工作不属于包可自动收尾的在途回调。
可重复执行的隔离回归及证据见 tests/process-stop/README.md。消费者升级时正常 Composer 更新并提交 lock,移除临时 path/软链接/源码替换;核对应用临时退出覆盖中的业务逻辑后清除重复实现和 process/signal 注册;按既有流程重建旧 DI/注解代理缓存,配置足够停止宽限并验证实际任务确认。不要手改 vendor。
WebSocket
相关目录:
src/Websocketpublish/websocket.php
主要能力:
- WebSocket 握手鉴权中间件。
- WS Controller 注解和消息路由。
- 生命周期 Hook:握手前/中/后、open、close、heartbeat、message、send 等。
- 连接绑定策略:
bindDimensions()、addressableDimensions()、onlineCheckDimensions()、uniqueDimensions()。 - 按维度推送:单 fd、批量 fd、广播、按业务维度推送。
- 连接唯一性:支持同账号单连接、同账号同设备单连接等踢旧策略。
- 实时在线检查:
checkRealtimeOnlineByDim(),适合小批量、强实时、unique 维度。 - 心跳在线检查:
checkHeartbeatOnlineByDim(),基于 Redis 7.4+HEXPIRE和 presence bucket 索引,适合大批量心跳级判断。 - 全量心跳在线快照:
checkHeartbeatOnlineAllByDim(),适合后台/统计类低频场景。 - per-server 实时消息队列和可选独立控制队列,避免在线核验/断连阻塞真实消息下发。
维度配置建议:
addressableDimensions()用于“能不能按这个维度推送/寻址”。onlineCheckDimensions()用于“能不能按这个维度做心跳在线检查”。uniqueDimensions()用于“这个维度是否单连接/踢旧”。- 低基数维度如
device_type=ios/android/h5可以用于推送寻址,但不应放入onlineCheckDimensions(),否则单个 value 可能挂大量连接,在线检查会变重。 - 高基数且单 value 连接数可控的维度,如
account_id,更适合做在线检查。
WebSocket 配置见 config/autoload/websocket.php,模板来源为 publish/websocket.php。重点配置:
key_prefix: WS Redis key 前缀。local_enable: local 环境是否启用 WS 常驻进程。server_set_cache_ms: 在线服务器集合短缓存。presence_bucket_num: 心跳 presence 索引 bucket 数。realtime_online.max/realtime_online.timeout: 实时在线核验批量上限与等待超时。queue: 实时消息队列消费进程与并发配置。dedicated_queue: 独立控制队列配置。
示例:
examples/WebSocket
JsonRpc / MQ RPC
相关目录:
src/JsonRpc
主要能力:
- JSON-RPC 客户端代理。
- MQ 异步 RPC 封装。
- MQ RPC 失败重试和异常链保留。
JSON-RPC 默认使用 hyperf/serializer 的 SimpleNormalizer,返回值为数组,不会按返回类型还原成 PHP 对象。需要还原对象时:
- 当前 Hyperf 3.2.0 的
Hyperf\Serializer\Serializer与 symfony/serializer 7.x 签名不兼容(加载即 Fatal),须先在项目里把symfony/serializer、symfony/property-access限定为^6.4; - 再在项目
config/autoload/dependencies.php中绑定(hyperf/serializer 自身也绑定了该接口且加载在本包之后,包内覆盖不住):
Hyperf\Contract\NormalizerInterface::class => new Hyperf\Serializer\SerializerFactory(Hyperf\Serializer\Serializer::class),
示例:
examples/Tools/UsageExamples.php
工具类
相关目录:
src/Toolssrc/Traitssrc/common/Functions.php
常用能力:
DcsLock: Redis 分布式锁,支持等待、自动续期、Lua 解锁。HttpClient: 协程内走 Swoole Coroutine Client,非协程内走 curl。postInternal使用进程内 Swoole HTTP 连接池;getPooled/postPooled/putPooled为渠道申请、查询的显式接入,原get/post/put/delete/patch不变。池按 origin 和传输设置隔离,每次独占连接,不共享请求 Header、正文或 Cookie。- 无连接数上限、无抢连接队列;空闲连接不足立即创建。无探活池通过密集业务逐步建立保底,保底内轮换复用;流量间隔不足以在空闲有效期内维持保底时,优先复用健康连接;参考最近两次间隔,避免门限两侧抖动造成反复冷连。补足保底的新建失败后,后续独立业务在 2 秒内优先复用已有健康连接,当前失败请求不重发。旧
max_connections/wait_timeout不再使用。min_connections统计真实已连接的忙碌与空闲连接,不要求始终保留指定数量的空闲连接。默认内部 5、渠道 2。 http_client.internal_pool/http_client.driver_pool可设置min_connections、connect_timeout(默认 5 秒)、max_idle_time(默认 60 秒)、heartbeat(默认 20 秒)、max_lifetime(默认 600 秒,0 关闭)。连接自建立起在max_lifetime的最后 10% 区间内(随机错开,避免同批连接同时重建),完成当前请求即关闭、不再放回池中,空闲的到期连接由后台扫描关闭,后续业务或安全探活新建连接,使 DNS 缓存更新后的地址有机会生效(不强制清除 DNS 缓存);进行中的请求不被打断,也不重发。借出时和后台每秒扫描都检查空闲期限;后台回收超出保底且超过空闲期限的连接;无安全健康路径时,保底连接也遵守空闲期限,由后续正常业务补建;健康探活不延长业务空闲期限。所有池内时间间隔/轮次使用单调时钟 hrtime,不受系统墙钟回拨影响;业务签名时间戳不变。配置在首次建立池时读取,变更须重启进程。- 内部默认
health_path='/'、health_method='GET',调用方须保证该路径安全。渠道默认没有健康路径,靠实际业务逐步建立保底连接;远端关闭或长时间无业务时不保证有效数量,不发送支付、查询或 Token 心跳。URL 主机名与健康配置 origin 键均按小写匹配。仅对已确认安全且可保持连接的目标配置health_targets['https://api.example.com:443']=['path'=>'/health','method'=>'HEAD'];仅支持 GET/HEAD,不支持的方法按未配置探活处理,路径不得带查询、片段或其他 origin。可将内部health_path设为 null 禁用探活。 - 维护只借用空闲连接,补建失败按轮次采用 2/4/8/16/30 秒退避,同一轮多条失败只计一次;已有连接的心跳不随补建退避停止,也不改变补建退避;上一批补建全部结束前不派发新批次,防错峰超时架空退避;探活响应必须完整且连接可继续使用,HTTP 状态本身不是业务或健康成功判断。
ElasticHttpPool::statistics()提供进程内 live/idle/busy、保底满足情况、补建失败轮数(maintenanceFailures)和维护运行状态。维护协程创建遇到容量上限时不阻断当前业务,计数/连接正常归还,后续业务再尝试启动。维护异常按 ERROR 记录,不永久关闭池;下一次业务可重启维护并继续复用健康连接。新建业务连接成功或业务补足内部保底时清除历史补建退避,旧探活结果不回写该状态。随 worker 退出关闭空闲连接,业务连接在请求完成后关闭。 - 禁用重定向及底层重发(
max_retries=0,不允许配置覆盖);正常 4xx/5xx 保留状态与正文。Swoole 6.1 返回 1xx 时无法继续收取该请求的最终响应,按结果未确认返回 errCode=-1 并关闭连接,不回池、不重发。失败连接及有残留可读数据的空闲连接立即关闭,不允许 TLS EOF 后被再次判定可用,本次请求不补发;协程池多值 Header 按 HTTP 列表合并(Cookie 分号、其他逗号),默认头按大小写无关覆盖;HTTP 方法按合法 token 检查,阻止请求行空格/换行注入;Header 的 CR/LF 在发送前拒绝,避免破坏请求边界;下一次独立业务可新建。业务配置/代码异常继续抛出,后台维护异常按 ERROR 记录。请求超时、读写超时、同步收响应、keep-alive、Header 小写和零重试不允许 transport 配置覆盖。内部协程响应头维持多值数组格式,非协程 cURL 分支保持原格式。内部 TLS 强制验证证书和主机名;普通渠道默认同样验证,SDK 用requestSdkPooled保留自身 Header 和显式 TLS 设置。 - HTTPS 默认验证受信任证书链及目标主机名:连接池、普通协程请求和非协程 cURL 三条路径一致;不允许自签名证书。使用系统 CA,无需逐个保存渠道证书。cURL 的显式 verify 参数和池的显式传输配置仍按调用契约保留,但不能将关闭校验作为一般网络错误的兜底。连接超时 5 秒与校验开关独立。SDK 专有传输仍须核对其自身配置。
- 非协程路径保持原生 curl 单次请求,不使用连接池。签名、AES/RSA、nonce、业务身份由调用方负责。应用接入前先发布包含新方法的本包再更新依赖,不能先发布调用新方法的应用。
php examples/internal-http-pool-check.php /path/to/app/vendor/autoload.php覆盖 TCP 复用、64 路并发、Header/body/Cookie 隔离、状态、禁用重定向、超时/断连不重发;elastic-http-pool-check.php覆盖真实活动保底、64 路扩容、超额回收和内部预热。http-pool-capacity-check.php在实际框架错误处理器下覆盖协程容量耗尽及恢复;http-pool-tls-check.php覆盖正常 TLS 1.3 复用和无 close_notify 的 TCP FIN 重复检查。http-pool-floor-check.php覆盖补足保底失败后独立请求仍使用健康连接;http-pool-maintenance-check.php注入协调器异常,覆盖维护故障记录、恢复和 TCP 复用。http-pool-stagger-check.php以错峰关闭/延迟响应验证补建批次不重叠及退避正确。http-pool-clock-check.php模拟墙钟回拨,确认池内时序与 TCP 复用不受影响。http-pool-timeout-check.php覆盖同一 TCP 上长短请求的独立超时。均仅回环,不代表生产吞吐或渠道验收。
OpenSslCrypt: AES/DES 对称加解密。OpenSslRsa/OpenSslRsa2: RSA 分块加解密。两者协议不同——OpenSslRsa密文为 hex(偏长),OpenSslRsa2密文为 base64(约短一半)。接口加密的Client-Key(AES 密钥)解密走OpenSslRsa2,客户端须使用同款算法加密。CheckVal/CheckParams: 常用格式校验和参数校验。Client/Server: 客户端信息、IP、设备、语言、路由、traceId 等辅助。ArrayTool、Strings、TimeTool、Distribution等工具。ObjectAttribute: 对象属性填充/导出辅助。
示例:
examples/Tools/UsageExamples.php
examples 目录
examples/ 是使用示例集合,不会随包安装自动执行。
安全边界:
composer.json只 autoloadsrc/,不加载examples/。ConfigProvider只扫描src/,不扫描examples/。- 示例命名空间为
Dleno\CommonCore\Examples\...,不会占用业务App\...。 - AMQP Consumer 示例的
isEnable()保留AMQP_ENABLE前置门禁;Crontab 示例保留ENABLE_CRONTAB前置门禁;随后都会拦截local环境并默认return false,避免误扫后真实执行。 - HTTP Controller、WS Controller 示例刻意不直接声明
#[AutoController]、#[WsController],复制到业务项目后再按注释启用。
使用方式:
- 复制需要的示例到业务项目
app/下对应目录。 - 修改 namespace,例如改成
App\WebSocket\Bind。 - 根据业务把
isEnable()改成自己的启用条件;复制后建议保留AMQP_ENABLE/ENABLE_CRONTAB等功能开关和local环境强制关闭判断。 - 不要直接把
vendor/dleno/common-core/examples加入业务注解扫描路径。
常用环境变量
基础开关:
APP_NAME: 应用名,Redis key 前缀等通用配置会使用。APP_ENV: 当前运行环境;存在.env.<APP_ENV>时会覆盖.env中的同名变量。IS_PROD: 强制判定为正式环境;未设置时config('app_env') === 'prod'也会视为正式环境。正式环境的APP_ENV必须写成prod(写成production等其他值会被当作非正式环境,Client-Debug请求头可跳过签名/解密/登录校验)。ENABLE_HTTP: 是否启用 HTTP server 相关基础中间件。ENABLE_WS: 是否启用 WebSocket 相关进程和中间件。未启用(或 local 未开websocket.local_enable)时,WS 消费进程不注册、不构造队列驱动,未配置async_queue的项目也可正常启动,无需在业务侧清除进程注解。HTTP_INIT_MIDDLEWARE_ENABLE: 是否自动注入 HTTP 初始化中间件和模块前置中间件,默认开启。WS_AUTH_MIDDLEWARE_ENABLE: 是否自动注入 WS 鉴权中间件,默认开启。LOG_MAX_FILES: 默认日志文件保留数量。
WebSocket:
WS_KEY_PREFIXWS_LOCAL_ENABLEWEBSOCKET_COMPRESSIONWS_SERVER_SET_CACHE_MSWS_PRESENCE_BUCKET_NUMWS_REALTIME_ONLINE_MAXWS_REALTIME_ONLINE_TIMEOUTWS_CONSUMER_PROCESSESWS_CONSUMER_LIMITWS_CONSUMER_MAX_MESSAGESWS_DEDICATED_QUEUE_ENABLEWS_DEDICATED_PROCESSESWS_DEDICATED_LIMITWS_DEDICATED_MAX_MESSAGES
连接池与外部服务:
DB_HOSTDB_MIN_CONNECTION/DB_MAX_CONNECTIONDB_CONNECT_TIMEOUT/DB_WAIT_TIMEOUTDB_HEARTBEAT/DB_MAX_IDLE_TIMEDB_READ_HOST/DB_WRITE_HOSTDB_READ_MIN_CONNECTION/DB_READ_MAX_CONNECTIONDB_READ_CONNECT_TIMEOUT/DB_READ_WAIT_TIMEOUTDB_READ_HEARTBEAT/DB_READ_MAX_IDLE_TIMEDB_WRITE_MIN_CONNECTION/DB_WRITE_MAX_CONNECTIONDB_WRITE_CONNECT_TIMEOUT/DB_WRITE_WAIT_TIMEOUTDB_WRITE_HEARTBEAT/DB_WRITE_MAX_IDLE_TIMEREDIS_MIN_CONNECTION/REDIS_MAX_CONNECTIONREDIS_CONNECT_TIMEOUT/REDIS_WAIT_TIMEOUTREDIS_HEARTBEAT/REDIS_MAX_IDLE_TIMEAMQP_CONNECTION/AMQP_MIN_CONNECTION/AMQP_MAX_CONNECTIONAMQP_CONNECT_TIMEOUT/AMQP_WAIT_TIMEOUTAMQP_READ_WRITE_TIMEOUT/AMQP_HEARTBEAT(协议心跳必须为正整数,不能为0;配置帮助类和工厂均默认心跳15秒、读写超时30秒,工厂仅补缺失值)AMQP_MAX_IDLE_CHANNELSCONSUL_SERVER_URIRPC_CONNECT_TIMEOUT/RPC_RECV_TIMEOUTRPC_MIN_CONNECTION/RPC_MAX_CONNECTIONRPC_WAIT_TIMEOUT/RPC_HEARTBEAT/RPC_MAX_IDLE_TIME
测试与校验
本包当前 composer test 执行 PHP 语法检查,覆盖 src、publish、examples:
composer test
Composer 文件校验:
composer validate --strict
静态分析(PHPStan level 1,配置见 phpstan.neon):
composer analyse
目录结构
src/ 核心源码
publish/ 可发布配置模板
examples/ 使用示例,不会自动加载或执行
.github/workflows/ Release 等自动化工作流
设计原则
- 核心协议和运行时约束放在 common-core 内统一维护。
- 业务差异通过策略、Hook、配置和示例复制接入。
- WebSocket 低层队列、注册、presence、在线检查逻辑不建议业务继承改写。
- 启动期能确定的错误尽量 fail-fast,但 Redis 暂时不可达只告警,避免误杀编排过程中的短暂抖动。
- 示例代码只作为模板,不作为运行时功能的一部分。