Skip to main content

Action Dispatch - Python 版本

高性能的基于装饰器的 Action 注册与分发系统

特性

与 Rust 版本功能对等:

  • 声明式注册:使用 @action 装饰器
  • 正则匹配 + 优先级:灵活的路由规则
  • 全局同步模式:支持 sync=True 全局排他执行
  • 分层匹配优化:精确匹配 O(1)、前缀匹配 O(m)、正则匹配 O(k)
  • RwLock 优化:并发执行 sync=False 的 action
  • 类型提示:完整的类型注解
  • 引用传递:Python 默认就是对象引用,零拷贝

安装

需要 Python 3.7+

# 无需额外依赖,只需标准库
python action_dispatch.py  # 运行测试

快速开始

from action_dispatch import dispatcher.action, dispatch

@dataclass
class UserEvent:
    user_id: int
    action: str

# 普通 action(可并发)
@action(regex=r'^user/\d+/read$', priority=5, sync=False)
def handle_read(event: UserEvent):
    print(f"读取用户: {event.user_id}")

# 关键 action(全局排他)
@action(regex=r'^user/\d+/update$', priority=10, sync=True)
def handle_update(event: UserEvent):
    print(f"更新用户: {event.user_id} (独占执行)")
    import time
    time.sleep(2)  # 模拟耗时操作

# 分发事件
dispatch("user/123/read", UserEvent(123, "read"))
dispatch("user/456/update", UserEvent(456, "update"))

API 文档

@action 装饰器

@action(regex: str, priority: int = 0, description: str = "", sync: bool = False)

参数

参数 类型 必需 默认值 说明
regex str - 匹配 key 的正则表达式
priority int 0 优先级,数值越大越高
description str "" 描述信息
sync bool False 是否全局同步模式

dispatch 函数

dispatch(key: str, event: Any) -> None

执行流程

  1. 获取读锁进行匹配(允许并发)
  2. 查找匹配的 action(分层匹配优化)
  3. 根据 sync 标志执行:
    • sync=False:保持读锁执行(并发)
    • sync=True:升级为写锁执行(独占)

异常

  • NoMatchError:没有匹配的 action
  • LockPoisonedError:锁被污染

list_actions 函数

list_actions() -> List[ActionInfo]

返回所有已注册的 action 信息(按优先级降序)。

性能优化

1. 分层匹配

系统自动分析正则表达式,选择最优匹配策略:

匹配类型 正则格式 时间复杂度 示例
精确匹配 ^literal$ O(1) ^user/123$
前缀匹配 ^prefix.* O(m) ^api/v1/.*
复杂正则 其他 O(k) ^user/\d+$

推荐:尽量使用精确匹配或前缀匹配

# ✅ 推荐:精确匹配(最快)
@action(regex=r"^user/profile$")
def handle_profile(event): pass

# ✅ 推荐:前缀匹配(快)
@action(regex=r"^api/v1/.*")
def handle_api(event): pass

# ⚠️ 可接受:简单正则
@action(regex=r"^user/\d+$")
def handle_user_id(event): pass

# ❌ 避免:复杂正则
@action(regex=r"^(?:user|admin)/(?:profile|settings)/\d+$")
def handle_complex(event): pass  # 慢

2. RwLock 并发

使用读写锁优化并发性能:

  • sync=False:持有读锁执行,多个可并发
  • sync=True:持有写锁执行,独占全局

性能对比(10 线程):

模式 使用 Mutex 使用 RwLock 提升
sync=False 串行(~1s) 并发(~100ms) 10x
sync=True 串行(~2s) 串行(~2s) 无差异

3. 零拷贝

Python 的对象传递默认就是引用传递,无需额外配置

# Python 自动使用引用传递,零拷贝
@action(regex=r"^large/.*$")
def handle_large(event: LargeEvent):
    # event 是引用,不会拷贝数据
    pass

性能测试

运行并发测试:

python test_concurrent.py

预期输出

【测试 1】分层匹配性能
精确匹配: 10000 次 dispatch
  总耗时: 150.00 ms
  平均耗时: 15.00 μs/次

前缀匹配: 10000 次 dispatch
  总耗时: 200.00 ms
  平均耗时: 20.00 μs/次

复杂正则: 10000 次 dispatch
  总耗时: 500.00 ms
  平均耗时: 50.00 μs/次

【测试 2】并发执行(sync=False)
5 个并发任务总耗时: 100 ms
✓ 由于并发执行,总耗时约等于单个任务时间(~100ms)

【测试 3】独占执行(sync=True)
3 个独占任务总耗时: 600 ms
✓ 由于串行执行,总耗时约等于 3 × 单个任务时间(~600ms)

性能对比

Python vs Rust

指标 Python Rust 说明
dispatch 耗时(精确匹配) ~15 μs ~0.1 μs Rust 150x 更快
dispatch 耗时(前缀匹配) ~20 μs ~5 μs Rust 4x 更快
dispatch 耗时(复杂正则) ~50 μs ~10 μs Rust 5x 更快
内存占用 ~10 MB ~200 KB Rust 50x 更小
并发能力 受 GIL 限制 真正并发 Rust 更强

结论

  • Python 版本已经过优化,性能不错
  • Rust 版本性能更优(10-150x),内存更小
  • Python 版本更易用,适合原型开发
  • Rust 版本适合生产环境和高性能场景

Python 特有的优化

1. GIL 优化

Python 有全局解释器锁(GIL),但我们的实现仍然有优化:

  • I/O 密集型:释放 GIL,真正并发
  • CPU 密集型:受 GIL 限制,但分层匹配仍能提速

2. 使用 PyPy

使用 PyPy 可以获得 2-5x 的性能提升:

pypy3 action_dispatch.py  # 使用 PyPy 运行

3. 使用 Cython

可以将核心模块编译为 C 扩展,获得接近 C 的性能:

# 安装 Cython
pip install cython

# 编译
cython action_dispatch.py  # 生成 .c 文件
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing \
    -I/usr/include/python3.x action_dispatch.c -o action_dispatch.so

注意事项

1. GIL 限制

Python 的 GIL 限制了真正的并发:

  • CPU 密集型任务:RwLock 提升有限
  • I/O 密集型任务:RwLock 提升显著

2. 避免死锁

不要在 sync=True 的 handler 中再次调用 dispatch

# ❌ 错误:会死锁
@action(regex=r"^task1$", sync=True)
def bad_handler(event):
    dispatch("task2", event)  # 死锁!

# ✅ 正确:使用 sync=False
@action(regex=r"^task1$", sync=False)
def good_handler(event):
    dispatch("task2", event)  # OK

3. 类型提示

虽然提供了类型提示,但 Python 运行时不强制检查:

from typing import TypeVar, Generic

T = TypeVar('T')

# 可以添加运行时类型检查
def dispatch_typed(key: str, event: T) -> None:
    if not isinstance(event, expected_type):
        raise TypeError(f"Expected {expected_type}, got {type(event)}")
    dispatch(key, event)

进阶用法

1. 性能监控

from action_dispatch import dispatch_with_stats, get_stats, reset_stats

# 使用带统计的 dispatch
for _ in range(1000):
    dispatch_with_stats("user/123", event)

# 获取统计信息
stats = get_stats()
print(f"平均耗时: {stats.average_time_us():.2f} μs")
print(f"总调用: {stats.total_dispatches}")

2. 自定义错误处理

from action_dispatch import dispatch, NoMatchError

try:
    dispatch("unknown/key", event)
except NoMatchError:
    # 记录日志或回退处理
    logger.warning(f"No handler for: unknown/key")
    fallback_handler(event)

3. 动态注册

from action_dispatch import dispatcher.action

# 动态创建 handler
def create_handler(name: str):
    @action(regex=f"^{name}/.*$", priority=5)
    def handler(event):
        print(f"Handle {name}: {event}")
    return handler

# 批量创建
for name in ["user", "order", "payment"]:
    create_handler(name)

文件结构

py/
├── action_dispatch.py      # 核心实现
├── test_concurrent.py      # 并发测试
└── README.md              # 本文档

许可证

MIT OR Apache-2.0


Happy coding with Python! 🐍

Release files for dispatcher3 0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dispatcher3 0.1
File Size Uploaded
dispatcher3-0.1.tar.gz 6.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dispatcher3 0.1
File Interpreter ABI Platform
dispatcher3-0.1-py3-none-any.whl Python 3 none any Details

Total release size: 10.9 kB

Release files / dispatcher3-0.1.tar.gz

Download URL dispatcher3-0.1.tar.gz
Size 6.0 kB
Tags Source
SHA-256 checksum
How to use checksums
418ae416a356fce5cfd804f2d867b02b36ba1083f77a816e00d5b8969900ce38
BLAKE2b-256 checksum
How to use checksums
dc9c0a6e0d6e3650b77ef50a0893ecc5fbdf628a456bc175f3f36d49295dd66f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.8

Release files / dispatcher3-0.1-py3-none-any.whl

Download URL dispatcher3-0.1-py3-none-any.whl
Size 4.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3899c4bd7f708b683dcb5c4c8068a0042c08e2896562ffa5811987c7afdcd039
BLAKE2b-256 checksum
How to use checksums
fd6b67912b46740289595a29e915737be77bf58c1818296e747af94a601acf57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.8

Release history Release notifications | RSS feed

This release

0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page