mbaynton / batch-framework
一个API和基础算法,用于高效处理可以分解为小工作单元的长运行任务。
Requires
- php: >=5.4.0
- psr/http-message: ^1.0
Requires (Dev)
- guzzlehttp/psr7: ^1.3
- phpunit/phpunit: ~4.8
- satooshi/php-coveralls: ^1.0
This package is auto-updated.
Last update: 2024-09-29 05:08:43 UTC
README
这个库提供基础算法和结构,以支持将可分解为小工作单元的长运行任务通过在Web服务器上调用PHP脚本来逐步处理。这避免了在Web执行环境中常见的脚本执行时间和网络超时限制。
它强调框架本身的最低开销,以便尽可能快地完成任务。
特性包括
- 在Web环境中运行时,支持在整个请求生命周期中处理工作单元批次。这防止了单个响应和Web服务器进程运行时间超过预期。
- 根据过去工作单元的运行时间来高效地确定何时停止运行更多工作单元,以便请求在目标时间内完成。
- 关注最小化涉及请求之间的移交所涉及的状态数据和访问后端存储的次数。
- 支持并行执行那些可并行化的尴尬问题,例如,在执行过程中,各个工作单元之间不需要相互通信或协调的情况。有关并行化详情,请参阅。
- 不需要使用特定的PHP框架,但了解控制器和服务设计模式。
作为库,它不提供“开箱即用”的功能。
依赖
- PHP 5.4+
Psr\Http\Message\ResponseInterface
可通过Composer获得,以及该接口的任何实现。
文档/示例
这里的文档将帮助您开始编写与该框架一起工作的代码。如果您遇到此处信息的不完整或疑问,您可能想参考GitHub上的Curator应用程序,它使用并编写了此框架。
文档针对 v1.0.0
版本准确无误。
术语及其定义
- 可运行:
用户实现的一个类,它模拟一个长运行的任务。可运行实例模拟并提供单个工作单元的实现。它的run()
方法执行实际的工作/计算,以推进任务的进度。 - 可运行迭代器:
一个PHP\Iterator
(请扩展AbstractRunnableIterator
),它根据给定的输入(例如Runner rank
和先前Runner
实现上已执行的可运行数量)生成适合于应执行的整体任务的段的可运行实例。 - 运行器:
服务器端代码运行任务。运行器为新的可运行实例泵送可运行迭代器,启动它们,监控可运行的运行时间和剩余时间,以决定何时停止,将可运行和任务执行事件分发给任务和控制器回调,并启动可运行和任务中间结果的聚合。 - 运行器ID:一个整数,唯一标识一个给定的逻辑
运行器
。客户端应创建与框架当前任务实例状态
支持的相应运行器
请求一样多的请求,最初将客户端尚未使用的唯一整数ID分配给这些请求。 - 运行器实现:
从逻辑上讲,该框架试图创建一种假象,即由x
个Runners
(如果x > 1
则并发执行)执行n
个Runnable
工作单元。然而,为了防止启动Runnable
的HTTP请求在期望的时间内未完成,框架可能会停止启动新的Runnable
,让Runner
提前停止工作,并通知客户端使用相同的Runner id
发起后续请求。每个由携带相同Runner id
的Runner
启动并处理的HTTP请求被称为该id的化身。所有Runner
的化身也将共享相同的Runner rank
。 - Runner rank:
在任务中唯一标识一个给定Runner
的数字。如果你的任务只支持一个并发Runner
,则该值始终为0
。如果你的Task
声明支持n
个并发Runner
,则该值从0
到n-1
不等。与Runner id
不同,其范围始终是0
到n-1
。 - Task:
用户实现的一个类,用于模拟长时间运行的任务。该Task
充当Runnable Iterator
的工厂,告诉框架如何处理Runnable
的结果,如果Runnable
遇到可抛出的错误或异常,可以介入,提供方法将多个Runnable
结果简化为更简单的中间结果,并提供方法将完整的Runnable
结果转换为Psr\Http\Message\ResponseInterface
。 - Task实例状态:
用户实现的一个类,用于模拟长时间运行的任务。Task实例状态捕捉了给定任务执行的变量属性,例如操作输入的位置、当前运行此Task
的用户(以PHP会话id表示)、估计的Task
大小(以Runnable
的数量表示)以及Task
支持的并发Runner
数量。通常,可以扩展TaskInstanceState
类,该类处理大多数事情,但你的任务独特输入除外。请注意,此类不打算用于捕获Runnable
输出。
此框架主要在AbstractRunner
类中提供了一个Runner
的实现。利用此库的完整系统通常包括扩展AbstractRunner
的实体,以与你的应用程序的持久层(例如数据库)接口,以及使用HttpRunnerControllerTrait
的控制器或其他脚本,用于处理传入请求并与你的应用程序会话层接口。
编写长时间运行的任务通常涉及设置以下组件
TaskInterface
的实现。- 扩展
AbstractRunnableIterator
以提供Runnables
。 - 执行工作单元的
RunnableInterface
的实现。 - 扩展
TaskInstanceState
以提供针对特定任务的输入属性。
并行化:使用多个runner
严格来说,此框架支持同时执行同一任务的多个runnable。但是,为了执行并发runnable,还需要大量的其他代码支持。
- 你的
AbstractRunner
扩展必须以并发安全的方式实现其方法,特别是AbstractRunner::retrieveRunnerState()
和AbstractRunner::finalizeRunner()
应在并发执行同一任务实例的多个实例时,以不会导致损坏或丢失写入的方式读取和写入其底层存储。 - 必须编写你的客户端以发送多个并发批量runner请求。
- 您想要执行的工作必须是令人尴尬地可并行化的。每个可运行的任务可以产生输出,但可运行的任务不能将其他可运行任务的输出作为输入,或者在其他可运行任务访问共享资源时相互干扰。
- 您的
任务实例状态
的getNumRunners()
必须返回大于1,以声明对超过1个运行器
的支持。 - 您通过
Task
构建的可运行迭代器
必须考虑运行器排名
,并尽可能均匀地将总可运行
的部分分配给每个运行器排名
,每个可运行
的工作单位恰好分配给一个运行器
。 - 您的整体应用程序(请求控制器等)不应受同一用户发起的多个同时请求的影响,并且在可运行执行时不应持有PHP会话锁。
为什么任务的最终结果总是HTTP响应?
将批量运行的总体结果打包成标准的HTTP响应格式,使应用程序能够接收请求并决定是否将请求延迟到批量任务。在任一情况下,客户端期望的HTTP响应最终都会生成。当客户端使用支持请求中间件和Promise模式的库实现时,这种方法效果很好。请求中间件会监视原始响应,这些响应表明需要批量任务。它不会用这个不完整的原始响应解决客户端应用程序代码的Promise,而是启动运行器
请求,直到获得结果HTTP响应,然后最终用这个响应解决原始Promise。
许可证
MIT