EventBus 文档¶
概述¶
EventBus 是事件分发中枢,负责任务队列、并发控制、错误上报及生命周期管理。Proxy 是对外发布事件的唯一接口。系统内置 ShutdownEvent 和 TaskErrorEvent 两个特殊事件。
EventBus¶
class EventBus:
def __init__(
self,
event_registry: EventRegistry,
handler_registry: EventHandlerRegistry,
queue: Optional[EventQueue] = None,
router: Optional[Router] = None,
max_handler_semaphore: int = 256,
shutdown: ShutdownConfig = ShutdownConfig(),
middleware_chain: Optional[MiddlewareChain] = None
) -> None
# 生命周期
async def start(self) -> None
async def stop(self) -> None
async def __aenter__(self) -> "EventBus"
async def __aexit__(self, ...) -> Optional[bool]
# 创建代理
def proxy(self, source: str, raw_event: Optional[Event] = None) -> Proxy
# 路由器
@property
def router(self) -> Router
# 可观测性
@property
def is_running(self) -> bool
@property
def is_publishing_enabled(self) -> bool
@property
def active_task_count(self) -> int
@property
def queue_size(self) -> int
构造参数¶
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
event_registry |
EventRegistry |
(必需) | 事件注册表实例。 |
handler_registry |
EventHandlerRegistry |
(必需) | 处理器注册表实例。 |
queue |
Optional[EventQueue] |
None |
内部派发队列,总线仅依赖 EventQueue 抽象。缺省为 InMemoryEventQueue()(容量取其自身默认配置 1024)。参见 事件队列文档。 |
router |
Optional[Router] |
None |
内部路由 / 分派表。缺省为 Router(event_registry),经 bus.router 暴露。参见 路由文档。 |
max_handler_semaphore |
int |
256 |
最大并发处理器数量(信号量)。 |
shutdown |
ShutdownConfig |
ShutdownConfig() |
停机行为配置,参见 ShutdownConfig。 |
middleware_chain |
Optional[MiddlewareChain] |
None |
中间件链,用于在发布流程中插入自定义逻辑。参见 中间件文档。 |
构造时自动注册 ShutdownEvent 和 TaskErrorEvent(若注册表中不存在),并内部创建 Router(经 bus.router 暴露)用于事件-处理器路由——处理器在激活(总线启动)时登记自己的订阅束。队列容量等配置由队列自身持有,总线不感知;需要自定义容量时注入 InMemoryEventQueue(InMemoryEventQueueConfig(maxsize=N))。中间件链在每次分发时按需构建,运行时增删即时生效。
生命周期¶
| 方法 | 说明 |
|---|---|
start() |
启动事件总线:先初始化中间件链(各中间件 on_setup),再激活处理器注册表(registry.activate(bus)——各处理器把订阅束登记进 bus.router),最后启动调度循环并开放发布——顺序与 stop() 的清理顺序互为镜像,处理器在激活阶段发布的消息也经过完整就绪的中间件管道。失败安全可重试:任一环节失败即整体回滚(撤销调度、下线处理器、回滚中间件)到干净的未启动态。重复调用安全;启动失败统一抛出 BusError 子类——回滚完整抛 BusStartFailed(携带 original_error 与 __cause__ 链),回滚不完整抛 BusStartRollbackError(聚合原始异常与处理器 / 中间件失败清单)。 |
stop() |
优雅停止:发布 __shutdown__ → 拒绝新发布 → 等待队列排空 → 取消调度 → 等待活跃任务完成 → 下线处理器注册表(registry.deactivate());下线失败会记录并汇总告警。重复调用安全。 |
async with EventBus(...) as bus: |
上下文管理器,自动启停。退出时 stop() 异常不会掩盖上下文体异常。 |
可观测性¶
| 属性 | 类型 | 说明 |
|---|---|---|
is_running |
bool |
总线是否在运行。详见 is_running。 |
is_publishing_enabled |
bool |
是否允许发布新事件。详见 is_publishing_enabled。 |
active_task_count |
int |
当前活跃的处理器任务数。 |
queue_size |
int |
事件队列中待处理的事件数。 |
is_running¶
指示事件总线的运行状态。其值在整个生命周期中的变化如下:
| 阶段 | is_running |
说明 |
|---|---|---|
构造后、start() 前 |
False |
总线已创建但尚未启动。 |
start() 执行中 |
False |
初始化中间件、激活处理器与创建调度循环中,尚未标记运行。 |
start() 完成后 |
True |
调度循环已启动,正在分发事件。 |
stop() 执行中 |
True |
正在排空队列、等待活跃任务完成。 |
stop() 完成后 |
False |
所有资源已释放。 |
关键:
is_running在stop()的整个排空和等待期间保持True, 直到所有活跃任务完成、中间件拆卸完毕后才会变为False。 这意味着在停机过程中,is_running=True但is_publishing_enabled=False(参见下文)。
bus = EventBus(reg, h_reg)
print(bus.is_running) # False
await bus.start()
print(bus.is_running) # True
# stop() 内部:排空队列 + 等待任务期间,is_running 仍为 True
await bus.stop()
print(bus.is_running) # False
is_publishing_enabled¶
指示是否允许向总线发布新事件。其值在整个生命周期中的变化如下:
| 阶段 | is_publishing_enabled |
说明 |
|---|---|---|
构造后、start() 前 |
False |
发布将抛出 RuntimeError。 |
start() 执行中 |
False |
中间件与处理器初始化中,发布尚未开放。 |
start() 完成后 |
True |
可以正常发布事件。 |
stop() 发布 __shutdown__ 后 |
False |
拒绝新事件入队,发布将抛出 BusShuttingDown。 |
stop() 完成后 |
False |
— |
关键:
is_publishing_enabled在stop()的第一时间被清除(紧随__shutdown__事件发布之后),早于队列排空和活跃任务等待。这确保了停机过程中不会有新事件 被加入队列,而已入队的事件仍会被正常分发处理。
与 is_running 的区别¶
| 场景 | is_running |
is_publishing_enabled |
|---|---|---|
| 正常运行 | True |
True |
| 停机中(排空队列、等待任务) | True |
False |
| 未启动 / 已停止 | False |
False |
这两个属性在停机过程中存在状态分离:is_running 保持 True 以确保队列和
任务被完整处理,而 is_publishing_enabled 提前变为 False 以阻止新事件流入。
使用场景¶
# 场景 1:健康检查端点
@app.get("/health")
async def health():
return {
"status": "ok" if bus.is_running else "degraded",
"can_publish": bus.is_publishing_enabled,
"queue_depth": bus.queue_size,
"active_handlers": bus.active_task_count,
}
# 场景 2:优雅停机时等待总线完全停止
async def graceful_shutdown():
signal.alarm("shutdown")
await bus.stop()
# 此时 is_running == False,可以安全退出进程
assert not bus.is_running
# 场景 3:发布前检查(通常无需手动检查,publish 会抛出对应异常)
async def safe_publish(bus, name, data):
if not bus.is_publishing_enabled:
logger.warning("总线已停止接受新事件,跳过发布")
return
await bus.proxy("my_service").publish(name, data)
状态转换时序图¶
start() stop()
│ │
│ dispatch_task 创建 │ 1. 发布 __shutdown__
│ _running.set() ◄────────── 仍在运行 ──────────► _running.clear()
│ _enable_publish.set() ◄── 允许发布 ──► _enable_publish.clear()
│ │
│ mw_chain.setup() │ 2. 排空队列(queue.join())
│ │ 3. 取消 dispatch_task
▼ ▼ 4. 等待活跃任务
is_running=True │ 5. mw_chain.teardown()
is_publishing_enabled=True │
▼
is_running=False
is_publishing_enabled=False
EventBus.Proxy¶
总线代理,唯一允许对外发布事件的接口。自动记录事件来源和处理链。
class Proxy:
async def publish(
self,
name: str,
data: Optional[Union[Dict[str, Any], BaseModel]] = None
) -> None
@property
def handlers_registry(self) -> EventHandlerRegistry
@property
def events_registry(self) -> EventRegistry
@property
def middleware(self) -> MiddlewareChain
| 成员 | 说明 |
|---|---|
publish(name, data=None) |
发布一个事件。data 可为字典或 Pydantic 模型实例。总线未运行时抛 RuntimeError,停止中抛 BusShuttingDown,未知事件抛 ValueError,负载类型不匹配抛 TypeError。 |
handlers_registry |
只读访问处理器注册表。 |
events_registry |
只读访问事件注册表。 |
middleware |
访问中间件链,支持运行时动态增删。变更即时生效。参见 中间件文档。 |
使用示例¶
# 基础发布
await bus.proxy("my_service").publish("user.login", {"user_id": "42"})
# 链式发布(事件转发)
class LoginHandler(EventHandler):
async def handle(self, payload, bus_proxy, raw_event):
await bus_proxy.publish("user.profile_loaded", profile_data)
ShutdownConfig¶
总线停机行为配置,用于控制优雅停止过程中的超时参数。
class ShutdownConfig(BaseModel):
queue_timeout_min: float = 1.0
queue_timeout_max: float = 15.0
tasks_timeout: float = 15.0
task_reclaim_delay: float = 0.0
avg_wait_time: float = 0.05
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
queue_timeout_min |
float |
1.0 |
队列排空最小等待时间(秒)。 |
queue_timeout_max |
float |
15.0 |
队列排空最大等待时间(秒)。 |
tasks_timeout |
float |
15.0 |
活跃处理器任务完成等待时间(秒)。 |
task_reclaim_delay |
float |
0.0 |
任务回收前等待完成回调(含任务异常日志)落定的时长(秒);0 表示仅让出一次事件循环。 |
avg_wait_time |
float |
0.05 |
每个事件平均处理时间估算(秒),用于动态计算队列排空超时。 |
实际队列排空超时 = max(queue_timeout_min, min(queue_timeout_max, queue_size × avg_wait_time))。
异常¶
| 异常类 | 说明 |
|---|---|
BusError |
总线异常基类,下列 Bus* 异常均为其子类。 |
BusShuttingDown |
总线正在停止时尝试发布新事件抛出。调用方应捕获并执行清理逻辑。 |
BusStartFailed |
总线启动失败且回滚完整抛出;携带 original_error(原始异常挂在 __cause__ 链上)。 |
BusStartRollbackError |
总线启动失败且回滚不完整抛出;聚合 original_error、handler_failures、middleware_failures。 |
RuntimeError |
总线尚未启动时尝试发布事件抛出。 |
ValueError |
未知事件名,或负载不匹配(声明要求负载但未传入、或声明无负载但传入了数据)。 |
TypeError |
传入的负载类型与事件声明中定义的 payload_type 不匹配。 |
内置事件¶
ShutdownEvent¶
class ShutdownEvent(EventDeclaration):
name = 'infinity_bus.__shutdown__'
# payload_type = None (无负载)
在 stop() 调用时自动发布(队列排空前)。处理器可订阅此事件执行清理工作。
TaskErrorEvent¶
class TaskErrorPayload(BaseModel):
error_event: Event
handler_id: Optional[str]
handler_name: str
error_type: str
error_message: str
class TaskErrorEvent(EventDeclaration):
name = 'infinity_bus.__task_error__'
payload_type = TaskErrorPayload
处理器抛出异常时自动发布。来源始终为 "EventBusErrorReporter"。可用于集中式错误监控。
class ErrorMonitor(EventHandler):
def __init__(self):
super().__init__(["infinity_bus.__task_error__"])
async def handle(self, payload, bus_proxy, raw_event):
if isinstance(payload, TaskErrorPayload):
logger.error(
"处理器 %s 在处理 %s 时失败: %s",
payload.handler_name,
payload.error_event.name,
payload.error_message,
)
使用示例¶
基础组装与启动¶
from infinity_bus import EventBus, EventRegistry, EventHandlerRegistry
# 1. 声明事件
class MyPayload(BaseModel):
message: str
class MyEvent(EventDeclaration):
name = "my.event"
payload_type = MyPayload
# 2. 注册事件
reg = EventRegistry()
reg.register(MyEvent)
# 3. 实现处理器
class MyHandler(EventHandler):
def __init__(self):
super().__init__(subscriptions=["my.event"])
async def handle(self, payload, bus_proxy, raw_event):
print(f"Received: {payload.message}")
# 4. 注册处理器
h_reg = EventHandlerRegistry()
await h_reg.register(MyHandler())
# 5. 启动总线并发布
async with EventBus(reg, h_reg) as bus:
await bus.proxy("cli").publish("my.event", {"message": "Hello"})
await asyncio.sleep(0.1) # 等待处理器输出
链式发布¶
处理器中可通过 bus_proxy.publish 发布新事件,形成处理链,总线会自动追踪来源:
class OrderHandler(EventHandler):
def __init__(self):
super().__init__(subscriptions=["order.created"])
async def handle(self, payload, bus_proxy, raw_event):
# 处理订单...
# 链式发布通知事件
await bus_proxy.publish("notification.send", {
"type": "order_confirmed",
"order_id": payload.order_id
})