Skip to main content

python library for dispatch action

Project description

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! 🐍

Project details


Release history Release notifications | RSS feed

This version

0.1

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dispatcher3-0.1.tar.gz (6.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dispatcher3-0.1-py3-none-any.whl (4.9 kB view details)

Uploaded Python 3

File details

Details for the file dispatcher3-0.1.tar.gz.

File metadata

  • Download URL: dispatcher3-0.1.tar.gz
  • Upload date:
  • Size: 6.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.8

File hashes

Hashes for dispatcher3-0.1.tar.gz
Algorithm Hash digest
SHA256 418ae416a356fce5cfd804f2d867b02b36ba1083f77a816e00d5b8969900ce38
MD5 b8ef67b7c384674edfb2359258af82fd
BLAKE2b-256 dc9c0a6e0d6e3650b77ef50a0893ecc5fbdf628a456bc175f3f36d49295dd66f

See more details on using hashes here.

File details

Details for the file dispatcher3-0.1-py3-none-any.whl.

File metadata

  • Download URL: dispatcher3-0.1-py3-none-any.whl
  • Upload date:
  • Size: 4.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.8

File hashes

Hashes for dispatcher3-0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3899c4bd7f708b683dcb5c4c8068a0042c08e2896562ffa5811987c7afdcd039
MD5 a64d61b5233956ddece1cc7e0f2b774a
BLAKE2b-256 fd6b67912b46740289595a29e915737be77bf58c1818296e747af94a601acf57

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page