通过一场架构访谈,介绍 OMBoxOS 模块化单体与独立后台 Worker 的设计,梳理进程隔离、任务硬超时、租约恢复、Provider 并发舱壁与熔断、systemd 资源预算,以及共享依赖、同步路径和服务器资源验收的边界。
本文是10月2日,yii框架开发者李工与omboxOS开发者周工的会议既要,经录音笔录音后,进行整理。
资深yii框架开发者 李工(以下简称李工):先给一个明确结论。今天的 OMBoxOS,是微服务,还是巨石应用?
OMBox OS开发者 周伟(以下简称周工):目前更准确的定位是“模块化单体,加上独立运行的后台 Worker”。主要业务仍然在同一个 PHP 框架中组织,后台任务由独立进程执行,项目数据库和存储仍然共享。
如果“巨石应用”指所有逻辑混在一起、一个任务出错就容易影响整个运行进程,那么我们已经建立了一批边界。但如果只是把一个主体应用统称为单体,OMBoxOS 仍然属于这个范畴。
我们已经具备部分将来拆分服务时可以复用的基础,例如分层、队列、任务执行记录和外部调用治理。不过,独立进程不等于独立服务。独立发布、数据归属、跨服务契约及运维体系,还需要围绕具体业务继续设计。
李工:为什么选择这样的架构?
周伟:我们的主要服务对象是小微企业,技术主线是 PHP 与 MariaDB。对这类项目,开发效率、部署成本和故障排查的可理解性非常重要。
我们先解决现实问题:长任务会不会拖住请求?单个任务耗尽内存以后,后续任务还能不能执行?某个外部供应商响应变慢,会不会占满调用资源?这些问题可以通过清晰的模块、任务队列和进程边界逐步解决。
当某项业务确实需要独立扩容、独立发布或独立的数据生命周期时,再依据已经明确的边界拆分,工程决策会更有依据。
李工:你们所说的“关注点分离”,具体分开了什么?
周伟:首先是请求处理与业务逻辑。Controller 接收输入、组织响应,Service 负责业务校验、权限前置、事务与流程编排,Repository 负责业务行数据访问。页面和组件消费准备好的展示数据。
其次是交互请求与长时间计算。Web 请求完成正常的身份和权限检查后,可以提交后台任务;任务保存必要的业务标识与执行上下文,再调用业务 Service。它不需要重建浏览器 Session,也不能借后台执行绕过权限检查。
第三是业务调用与外部供应商接入。业务通过框架能力链调用供应商,接入层集中处理凭据、超时和故障治理。这减少了各模块自行拼接请求、自行保存密钥、自行决定重试策略的情况。
这些是分层规则和主要执行链已经建立的边界,并不意味着每一处历史代码都已经完成治理。后续模块接入仍然需要检查实际调用链。
李工:后台任务的进程隔离,现在是怎样运行的?
周伟:在启用队列的执行路径中,常驻 Worker 负责领取任务和监督执行,每个领取到的任务由一个新的 PHP CLI 子进程运行。
父进程维护任务租约、监控当前执行,并处理停机信号。子进程从持久化任务记录中读取执行数据,结束后释放本次任务使用的进程内存。任务数据和供应商凭据不会作为命令行参数传递。
因此,任务中的 PHP 内存耗尽或异常退出,可以被限制在这一次子进程执行中。父 Worker 可以记录失败,并继续处理后续任务。这是我们已经通过真实故障测试验证的行为。
李工:如果任务一直卡住,连监督它的父进程都被数据库请求拖住了怎么办?
周伟:任务除了受到父进程监督,还受到独立的操作系统超时进程约束。即使父进程正在等待数据库心跳操作,这个超时监督也可以在墙钟期限到达后终止任务进程组。
终止对象包括同一进程组中的普通后代进程。停机时则先发送 TERM,给予配置好的退出宽限期,再对未退出的执行升级为 KILL。相关测试覆盖了忽略 TERM、监督回调阻塞,以及任务正常退出后留下未等待子进程等情况。
但终止进程不会撤销已经发生的业务副作用。如果任务已经向外部系统提交了请求,仍需通过业务幂等和结果核实处理后续状态。
李工:任务失败以后,系统能做哪些恢复工作?
周伟:统一任务运行时保存任务状态、每次执行尝试、日志、心跳、进度和检查点。任务通过租约领取,失败后可以按照策略重试,过期租约可以恢复。
领取指定队列时,过期租约恢复也限定在这个队列内。恢复逻辑会锁定并重新检查当前记录,避免依据旧扫描结果,把已经续租或完成的任务误判为过期。
这套机制采用“至少执行一次”的交付语义,业务必须具备幂等键或等效的防重复检查。检查点提供的是恢复依据,业务任务仍需明确怎样续跑、怎样避免重复生成、重复发放或重复扣减。
李工:“故障舱壁”听起来很形象,它在框架里对应什么?
周伟:可以把它理解为给不同工作设置容量和故障边界。
任务侧可以划分队列,例如消息、文档和能力调用,并在需要隔离时配置独立 Worker。不同队列可以分配不同的执行数量、PHP 内存预算和服务资源预算。实现这些配置以后,一个队列的长任务就不必与另一个队列共用同一个执行 Worker。
外部调用侧则按 Provider 设置共享并发额度。某个供应商的额度用完,新调用会立即收到明确的拒绝结果。其他供应商拥有各自的预算,可以继续准入。
这样的隔离需要落实到实际部署和调用链。仅仅给任务取不同的队列名称,或者框架中存在一个治理组件,都不足以证明所有业务已经获得隔离。
李工:并发舱壁与熔断有什么区别?
周伟:并发舱壁控制同时有多少个调用占用资源,熔断控制持续故障时是否继续向供应商发起调用。
当网络、超时或上游服务端故障连续达到阈值,熔断器会暂时拒绝新调用。冷却结束后,只允许一个恢复探测;探测成功,再恢复正常调用。这样可以减少持续故障期间的重复请求和资源消耗。
本地输入错误、授权错误和凭据校验错误不能简单计为供应商故障。否则,一个用户输入错误就可能影响其他人的正常调用。我们也对这类中性失败进行了测试。
李工:多进程之间怎样共享调用额度?进程突然被强杀,会不会永久占着名额?
周伟:当前实现面向同一台部署主机。接入治理的调用通过共享状态和操作系统文件锁协调额度,一次调用持续持有自己的槽位锁,直到执行结束。
如果进程被强杀,操作系统会释放文件锁,后续准入再回收遗留状态。我们不会仅凭预计超时时间到了,就把仍在执行的调用视为已经释放额度。
共享状态损坏或不可用时,治理层会拒绝调用,并返回明确错误码。这里也有一个重要边界:如果上游已经成功,但本地完成状态保存失败,调用方不能据此认定上游没有执行。涉及副作用的请求必须核实结果,不能直接自动重发。
这套文件锁方案没有跨主机的全局预算能力。将来部署多台应用服务器时,需要另行接入共享状态后端。
李工:超时与重试,是不是统一设置一个数字就可以了?
周伟:需要看完整调用过程。连接超时、单次请求超时和整段业务执行期限解决的是不同问题。
已经接入治理的直连 HTTP 调用关闭了客户端内部叠加重试;是否重试交给具备幂等和退避策略的任务或业务编排决定。对需要多次 HTTP 请求的高德距离矩阵,一次调用共用一个时间预算,每次请求按剩余时间收敛。
部分 SDK 只能设置请求级超时,不能据此声称整个 SDK 执行已经具备墙钟硬期限。耗时较长的 SDK 工作适合进入受独立超时监督的任务 Worker。
李工:进程隔离以后,为什么还需要 cgroup?
周伟:PHP 的内存限制主要约束 PHP 管理的内存,无法完整覆盖原生工具和整个后代进程树。cgroup 用于在操作系统层面限制整个服务的资源。
我们已经准备了 systemd 部署模板,分别配置内存压力阈值、内存硬上限、CPU 配额和任务数量上限。默认任务、消息、文档、备份和能力 Worker 采用不同预算。
在正常 Ubuntu 服务器上,这些资源控制由宿主机 systemd 管理,PHP 应用无需直接写 cgroup。Supervisor 模板提供 PHP 内存限制、Worker 回收和日志控制,但不能替代 systemd 的 cgroup 资源约束。
李工:这次测试环境的 cgroup 是只读,会影响对功能的判断吗?
周伟:它影响验收范围。当前沙箱不能创建和调整测试所需的 cgroup,所以我们不能在这里证明部署模板中的 CPU、总内存和任务数量上限已经实际生效。
已经验证的是 PHP 子进程隔离、任务硬超时、内存耗尽后的后续任务执行,以及 Provider 并发与熔断行为。systemd 配置通过了离线校验,仍需在可控制的测试服务器上做资源压力验收。
没有必要为了这些应用级测试,把整个沙箱的 cgroup 改为可写。如果确实需要在容器内管理子 cgroup,应由宿主机提供受控的可写子树与委派权限。容器内看到只读挂载,也不代表宿主机施加的资源限制失效。
李工:能否介绍一下已经完成的自动化测试?
周伟:截至本稿编写时,本轮围绕这些功能的自动化复测通过了 143 个测试、1,114 个断言,没有失败、错误或跳过。这个数字是本次指定范围的结果,不代表整个项目的全部测试。
Worker 的真实验收使用实际任务 CLI 和开发数据库中的随机测试队列,覆盖了四种情景:成功执行并保存检查点、墙钟超时后重试、子进程内存耗尽后父 Worker 继续执行、收到 TERM 后完成受控退出并停止领取新任务。
Provider 的真实多进程测试覆盖了共享并发上限、不同供应商预算隔离、强杀后的额度恢复、熔断冷却与单一恢复探测,以及损坏状态下的拒绝行为。测试不调用真实外部供应商。
同时,相关 PHP 语法检查、PHPStan 静态分析、框架边界检查,以及五类 systemd 配置的离线校验通过。测试探针与进程已经清理,原有 32 个任务的完整行快照在测试前后保持一致。
李工:现在还不能承诺哪些能力?
周伟:首先,共享数据库与存储仍然是共同依赖。数据库不可用、锁竞争或存储耗尽,可能影响多个队列。
其次,关闭队列后采用的请求内同步执行路径,不具备后台 Worker 的子进程边界和独立硬超时监督。现有隔离能力不能自动外推到所有同步请求。
第三,Provider 治理覆盖的是已经显式接入的调用链。新增 SDK、支付接口或其他直接出站调用,都需要检查接入情况。供应商故障治理也不等于自动完成跨供应商回退。
此外,部署模板尚未在本轮操作中安装或重启;服务器上的资源上限实测仍未完成。我们会将“已有实现”“通过验收”和“已经部署生效”分别说明。
李工:对于使用 OMBoxOS 的团队,这一阶段最实际的价值是什么?
周伟:团队可以用统一机制处理后台工作,而不必为每个模块单独设计任务执行、日志、超时和失败恢复。
任务异常有记录,单个执行有进程边界,外部供应商有并发预算和熔断规则,运维有队列与服务资源配置入口。遇到故障时,开发者能够沿任务记录、执行尝试、心跳和日志查找原因,也能明确哪些工作需要重试、核实或人工处理。
下一阶段最具体的验收工作,是在可控制的 Ubuntu 测试服务器上启用已准备的 systemd 配置,验证内存、CPU 和任务数量限制,并观察一个 Worker 受到资源压力时,其他 Worker 的实际表现。
评论 0
暂无评论,欢迎分享您的想法。
用户
留下您的思考
未登录(127.0.0.1)
使用已有账号登录后,即可参与讨论。
登录后发表评论 →