queue-func 0.2.1__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {queue_func-0.2.1 → queue_func-0.2.3}/PKG-INFO +45 -53
  2. queue_func-0.2.3/README.md +220 -0
  3. {queue_func-0.2.1 → queue_func-0.2.3}/pyproject.toml +2 -2
  4. queue_func-0.2.1/queue_func/broker/queue.py → queue_func-0.2.3/queue_func/backends/queue/broker.py +5 -5
  5. queue_func-0.2.1/queue_func/factory/builders/queue.py → queue_func-0.2.3/queue_func/backends/queue/build.py +3 -3
  6. queue_func-0.2.1/queue_func/config/queue.py → queue_func-0.2.3/queue_func/backends/queue/config.py +2 -2
  7. queue_func-0.2.1/queue_func/consumer/queue.py → queue_func-0.2.3/queue_func/backends/queue/consumer.py +3 -2
  8. queue_func-0.2.1/queue_func/producer/queue.py → queue_func-0.2.3/queue_func/backends/queue/producer.py +2 -2
  9. queue_func-0.2.1/queue_func/shared_resource/queue.py → queue_func-0.2.3/queue_func/backends/queue/resource.py +1 -1
  10. queue_func-0.2.1/queue_func/broker/rabbit_aio_pika.py → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika/broker.py +5 -5
  11. queue_func-0.2.1/queue_func/factory/builders/rabbit_aio_pika.py → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika/build.py +3 -3
  12. queue_func-0.2.1/queue_func/config/rabbit_aio_pika.py → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika/config.py +2 -2
  13. queue_func-0.2.1/queue_func/consumer/rabbit_aio_pika.py → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika/consumer.py +3 -2
  14. queue_func-0.2.1/queue_func/producer/rabbit_aio_pika.py → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika/producer.py +3 -3
  15. queue_func-0.2.1/queue_func/shared_resource/rabbit_aio_pika.py → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika/resource.py +1 -1
  16. queue_func-0.2.1/queue_func/broker/base.py → queue_func-0.2.3/queue_func/core/broker.py +4 -4
  17. queue_func-0.2.1/queue_func/config/general.py → queue_func-0.2.3/queue_func/core/config.py +9 -1
  18. queue_func-0.2.1/queue_func/consumer/base.py → queue_func-0.2.3/queue_func/core/consumer.py +9 -1
  19. queue_func-0.2.1/queue_func/producer/base.py → queue_func-0.2.3/queue_func/core/producer.py +1 -1
  20. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/factory/builders/__init__.py +2 -2
  21. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/factory/deco.py +3 -3
  22. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/factory/registry.py +1 -1
  23. queue_func-0.2.1/README.md +0 -228
  24. queue_func-0.2.1/queue_func/config/utils.py +0 -15
  25. queue_func-0.2.1/queue_func/utils/__init__.py +0 -1
  26. {queue_func-0.2.1 → queue_func-0.2.3}/LICENSE +0 -0
  27. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/__init__.py +0 -0
  28. {queue_func-0.2.1/queue_func/broker → queue_func-0.2.3/queue_func/backends}/__init__.py +0 -0
  29. {queue_func-0.2.1/queue_func/config → queue_func-0.2.3/queue_func/backends/queue}/__init__.py +0 -0
  30. {queue_func-0.2.1/queue_func/consumer → queue_func-0.2.3/queue_func/backends/rabbit_aio_pika}/__init__.py +0 -0
  31. {queue_func-0.2.1/queue_func/pool_executor → queue_func-0.2.3/queue_func/core}/__init__.py +0 -0
  32. /queue_func-0.2.1/queue_func/shared_resource/base.py → /queue_func-0.2.3/queue_func/core/resource.py +0 -0
  33. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/errors.py +0 -0
  34. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/factory/__init__.py +0 -0
  35. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/factory/backend.py +0 -0
  36. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/factory/runtime.py +0 -0
  37. {queue_func-0.2.1/queue_func/producer → queue_func-0.2.3/queue_func/pool_executor}/__init__.py +0 -0
  38. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/pool_executor/base.py +0 -0
  39. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/pool_executor/general.py +0 -0
  40. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/pool_executor/utils.py +0 -0
  41. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/serializer/__init__.py +0 -0
  42. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/serializer/base.py +0 -0
  43. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/serializer/orjson_ser.py +0 -0
  44. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/serializer/raw.py +0 -0
  45. {queue_func-0.2.1/queue_func/shared_resource → queue_func-0.2.3/queue_func/task}/__init__.py +0 -0
  46. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/task/base.py +0 -0
  47. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/task/thread.py +0 -0
  48. {queue_func-0.2.1/queue_func/task → queue_func-0.2.3/queue_func/utils}/__init__.py +0 -0
  49. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/utils/deco.py +0 -0
  50. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/utils/hook.py +0 -0
  51. {queue_func-0.2.1 → queue_func-0.2.3}/queue_func/utils/waiter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: queue-func
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Async coroutine functions as queue-backed tasks (QueueFunc).
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -17,7 +17,7 @@ Provides-Extra: rabbit
17
17
  Requires-Dist: aio-pika (>=9.6,<10.0) ; extra == "rabbit"
18
18
  Requires-Dist: orjson
19
19
  Requires-Dist: pydantic (>=2.14.1,<3.0.0)
20
- Project-URL: Changelog, https://github.com/sekil9529/queue-func/blob/master/Changelog.rst
20
+ Project-URL: Changelog, https://github.com/sekil9529/queue-func/blob/master/CHANGELOG.md
21
21
  Project-URL: Documentation, https://github.com/sekil9529/queue-func#readme
22
22
  Project-URL: Homepage, https://github.com/sekil9529/queue-func
23
23
  Project-URL: Issues, https://github.com/sekil9529/queue-func/issues
@@ -26,30 +26,36 @@ Description-Content-Type: text/markdown
26
26
 
27
27
  # queue-func
28
28
 
29
- 把 **`async def` 协程** 变成可 **`push`** 的队列任务:在 asyncio 里投递、消费,并按通道控制并发与背压。
29
+ [![Coverage Status](https://coveralls.io/repos/github/sekil9529/queue-func/badge.svg?branch=master)](https://coveralls.io/github/sekil9529/queue-func)
30
+ [![tests](https://github.com/sekil9529/queue-func/actions/workflows/tests.yml/badge.svg)](https://github.com/sekil9529/queue-func/actions?query=workflow%3Atests)
31
+ [![Latest Version](https://img.shields.io/pypi/v/queue-func.svg)](https://pypi.org/project/queue-func/)
32
+ [![Wheel](https://img.shields.io/pypi/wheel/queue-func.svg)](https://pypi.org/project/queue-func/)
33
+ [![Python Versions](https://img.shields.io/pypi/pyversions/queue-func.svg)](https://pypi.org/project/queue-func/)
34
+ [![License](https://img.shields.io/pypi/l/queue-func.svg)](https://pypi.org/project/queue-func/)
30
35
 
31
- 仓库:[github.com/sekil9529/queue-func](https://github.com/sekil9529/queue-func)。版本记录见 [Changelog.rst](Changelog.rst)。
36
+ 把一个 `async def` 收成队列任务:`push` 投递,消费者在同一个事件循环里执行。
32
37
 
33
- > **需要 Python 3.12 及以上。** 任务入口必须是协程函数,普通函数不能作为任务。
38
+ 仓库示例在 [examples/](examples/)。版本记录见 [CHANGELOG.md](CHANGELOG.md)。
39
+
40
+ > 需要 Python 3.12 及以上。任务必须是协程函数。
34
41
  >
35
- > RabbitMQ 后端需要另装可选依赖,底层使用 [aio-pika](https://github.com/mosquito/aio-pika) `^9.6`。
42
+ > RabbitMQ 后端是可选依赖,底层用 [aio-pika](https://github.com/mosquito/aio-pika) `^9.6`。
36
43
 
37
44
  ## 特点
38
45
 
39
- - 完全在 asyncio 里运行。消费者在同一事件循环里按上限并发执行任务,可以和 FastAPI / Starlette 放在一个进程里。
40
- - 一条链路是序列化、投递、反序列化、执行。启停由通道或 `QueueFuncRuntime` 显式管理。
41
- - 每个 `QueueFunc` 有自己的队列和消费并发上限。`QueueFuncRuntime` 可以同时跑多段流水线,各段互不影响。
42
- - 序列化器的 `pack` / `unpack` 决定队列里的数据结构。
43
- - 通过 `register_backend` 接入新的队列后端。
46
+ - 进程内 `asyncio.Queue` 和 RabbitMQ 两种后端,装饰器都是 `@queue`。
47
+ - 每个任务有自己的队列、序列化和消费并发上限。
48
+ - `QueueFuncRuntime` 把多段任务按下游先启动、上游后停止的顺序一起管。
49
+ - `start` 会等到消费者开始收消息再返回。
50
+ - 装饰之后仍可以直接 `await` 调用,不经过队列。
44
51
 
45
- ## 当前后端
52
+ ## 启动和停止
46
53
 
47
- | 后端 | 说明 |
48
- |------|------|
49
- | `Backend.QUEUE` | 进程内 `asyncio.Queue` |
50
- | `Backend.RABBIT_AIO_PIKA` | RabbitMQ,安装可选依赖 `rabbit` |
54
+ `broker.start_consumer()` 创建消费任务,并等到它开始收消息。内存队列是已经堵在 `get()` 上;RabbitMQ 是队列已经订阅上。
55
+
56
+ `stop` 先标上停止,再等手头这次收消息结束。空闲时最多再等一个 `consume_timeout`。还没取走的 RabbitMQ 消息留在代理上。
51
57
 
52
- 内存队列的消息留在进程里,序列化默认是 `raw`(队列中仍是 `(args, kwargs)`)。RabbitMQ 默认用 orjson,队列中是 `bytes`。
58
+ RabbitMQ 在消息交给消费池之后确认。处理函数出错不会把消息退回队列。`push` 会等发布确认,超时抛出 `queue_func.PublishTimeoutError`。
53
59
 
54
60
  ## 安装
55
61
 
@@ -63,7 +69,7 @@ pip install queue-func
63
69
  pip install "queue-func[rabbit]"
64
70
  ```
65
71
 
66
- 从仓库安装(开发):
72
+ 从仓库安装:
67
73
 
68
74
  ```shell
69
75
  git clone https://github.com/sekil9529/queue-func.git
@@ -73,6 +79,8 @@ poetry install -E rabbit
73
79
 
74
80
  ## 用法
75
81
 
82
+ 从 `queue_func.factory` 引入 `queue`、`Backend`、`QueueFuncRuntime`。
83
+
76
84
  ### 进程内队列
77
85
 
78
86
  ```python
@@ -96,9 +104,9 @@ async def main() -> None:
96
104
  asyncio.run(main())
97
105
  ```
98
106
 
99
- `push` 就是 `broker.publish`。不传 `timeout` 时用配置里的 `publish_timeout`;传入数字只作用于这一次。`None` 表示不限时。超时抛出 `queue_func.PublishTimeoutError`。
107
+ `push` 就是 `broker.publish`。不传 `timeout` 时用配置里的 `publish_timeout`;传入数字只对这一次有效。`None` 表示不限时。
100
108
 
101
- 装饰之后仍可直接调用,不经过队列:
109
+ 直接调用:
102
110
 
103
111
  ```python
104
112
  await process_order(1)
@@ -106,7 +114,7 @@ await process_order(1)
106
114
 
107
115
  ### 多段流水线
108
116
 
109
- `QueueFuncRuntime.start()` 按列表反序启动消费者,下游先就绪。`stop()` 先逐个停止消费者,再停止全部生产者。
117
+ `QueueFuncRuntime.start()` 按列表反序启动,下游先就绪。`stop()` 先逐个停消费者,再停全部生产者。
110
118
 
111
119
  ```python
112
120
  import asyncio
@@ -134,7 +142,7 @@ async def main() -> None:
134
142
  asyncio.run(main())
135
143
  ```
136
144
 
137
- 三段链式示例(含日志):
145
+ 三段链式示例:
138
146
 
139
147
  ```shell
140
148
  poetry run python examples/demo_queue.py
@@ -142,11 +150,9 @@ poetry run python examples/demo_queue.py
142
150
 
143
151
  ### RabbitMQ
144
152
 
145
- `url` 和 `queue_name` 必填。`enable_declare_queue=True` 时,队列不存在会创建。默认交换机名是 `""`,路由键默认用队列名。`push` 会等待发布确认;`publish_timeout` 是这次确认的超时。
146
-
147
- 消息交给消费池之后就确认。处理函数出错不会把消息退回队列。`stop` 会设上停止标志,当前这次拉取结束(空闲时最多再等一个 `consume_timeout`)后退出;还没取走的消息留在 RabbitMQ 上。
153
+ `url` 和 `queue_name` 必填。`enable_declare_queue=True` 时,队列不存在会创建。默认交换机名是 `""`,路由键默认用队列名。已经存在的队列再声明时,持久化等参数必须和代理上的一致。
148
154
 
149
- 同一个 `QueueFunc` 的生产者和消费者可以分进程运行。只投递的进程把 `enable_consumer` 设为 `False`。
155
+ 只投递、不消费的进程把 `enable_consumer` 设为 `False`。
150
156
 
151
157
  ```python
152
158
  import asyncio
@@ -159,6 +165,7 @@ from queue_func.factory import Backend, queue
159
165
  url="amqp://guest:guest@127.0.0.1/",
160
166
  queue_name="process_order",
161
167
  enable_declare_queue=True,
168
+ consume_timeout=0.5,
162
169
  )
163
170
  async def process_order(order_id: int) -> None:
164
171
  print(order_id)
@@ -187,19 +194,20 @@ poetry run python examples/demo_rabbit_aio_pika.py
187
194
  | 参数 | 作用 |
188
195
  |------|------|
189
196
  | `consumer_pool_maxsize` | 同时执行的协程数,默认 200 |
190
- | `consume_timeout` | 一次拉取的等待时间,默认 2 秒 |
197
+ | `consume_timeout` | 一次等消息的时间,默认 2 秒 |
191
198
  | `publish_timeout` | 投递等待上限,默认不限 |
192
199
  | `maxsize` | 仅内存队列,`asyncio.Queue` 容量,默认 200 |
193
- | `serializer_cls` | 队列中的数据结构 |
200
+ | `serializer_cls` | 队列中的数据结构。内存队列默认 `raw`,RabbitMQ 默认 orjson |
194
201
  | `prefetch_count` | 仅 RabbitMQ,未确认消息窗口 |
195
202
  | `delivery_mode` | 仅 RabbitMQ,默认持久化 |
196
203
  | `exchange_name` / `routing_key` | 仅 RabbitMQ |
204
+ | `queue_durable` | 仅 RabbitMQ,声明队列时是否持久化,默认否 |
197
205
 
198
- 其余字段见 `queue_func.config.queue.Config` 和 `queue_func.config.rabbit_aio_pika.Config`。
206
+ 其余字段见 `queue_func.backends.queue.config.Config` 和 `queue_func.backends.rabbit_aio_pika.config.Config`。
199
207
 
200
- ## 架构
208
+ ## 一条任务里有什么
201
209
 
202
- 一个 `@queue` 装饰的协程就是一条队列。`push` 投到它自己这条队列上。
210
+ `@queue` 装饰的协程就是一条队列。`push` 投到它自己这条队列上。
203
211
 
204
212
  - **Broker** 组装生产者、消费者和双方共用的资源,并提供启停与投递。
205
213
  - **Producer** 序列化后把数据写入队列。
@@ -218,36 +226,20 @@ flowchart TB
218
226
  POOL --> FN["业务协程"]
219
227
  ```
220
228
 
221
- ## 项目结构
222
-
223
- ```
224
- queue_func/
225
- ├── broker/
226
- ├── producer/
227
- ├── consumer/
228
- ├── serializer/ # raw、orjson
229
- ├── shared_resource/
230
- ├── config/
231
- ├── factory/ # @queue、QueueFunc、QueueFuncRuntime
232
- ├── pool_executor/
233
- └── utils/
234
- examples/
235
- ├── demo_queue.py
236
- └── demo_rabbit_aio_pika.py
237
- ```
238
-
239
- ## 开发与测试
229
+ ## 测试
240
230
 
241
231
  ```shell
242
232
  poetry install -E rabbit
243
- pytest
233
+ poetry run pytest
244
234
  ```
245
235
 
246
- `pytest.ini` 会生成覆盖率页面(`htmlcov/`)和 `report.html`。
236
+ 单元测试使用假的 RabbitMQ 连接,不要求本机有代理。`examples/demo_rabbit_aio_pika.py` 需要本机 `guest` / `guest`、端口 `5672`。
237
+
238
+ `pytest.ini` 会写出覆盖率页面 `htmlcov/` 和 `report.html`。
247
239
 
248
240
  ## 版本
249
241
 
250
- 遵循[语义化版本](https://semver.org/lang/zh-CN/)。变更记录在 [Changelog.rst](Changelog.rst)。
242
+ 遵循[语义化版本](https://semver.org/lang/zh-CN/)。变更记录在 [CHANGELOG.md](CHANGELOG.md)。
251
243
 
252
244
  ## 许可证
253
245
 
@@ -0,0 +1,220 @@
1
+ # queue-func
2
+
3
+ [![Coverage Status](https://coveralls.io/repos/github/sekil9529/queue-func/badge.svg?branch=master)](https://coveralls.io/github/sekil9529/queue-func)
4
+ [![tests](https://github.com/sekil9529/queue-func/actions/workflows/tests.yml/badge.svg)](https://github.com/sekil9529/queue-func/actions?query=workflow%3Atests)
5
+ [![Latest Version](https://img.shields.io/pypi/v/queue-func.svg)](https://pypi.org/project/queue-func/)
6
+ [![Wheel](https://img.shields.io/pypi/wheel/queue-func.svg)](https://pypi.org/project/queue-func/)
7
+ [![Python Versions](https://img.shields.io/pypi/pyversions/queue-func.svg)](https://pypi.org/project/queue-func/)
8
+ [![License](https://img.shields.io/pypi/l/queue-func.svg)](https://pypi.org/project/queue-func/)
9
+
10
+ 把一个 `async def` 收成队列任务:`push` 投递,消费者在同一个事件循环里执行。
11
+
12
+ 仓库示例在 [examples/](examples/)。版本记录见 [CHANGELOG.md](CHANGELOG.md)。
13
+
14
+ > 需要 Python 3.12 及以上。任务必须是协程函数。
15
+ >
16
+ > RabbitMQ 后端是可选依赖,底层用 [aio-pika](https://github.com/mosquito/aio-pika) `^9.6`。
17
+
18
+ ## 特点
19
+
20
+ - 进程内 `asyncio.Queue` 和 RabbitMQ 两种后端,装饰器都是 `@queue`。
21
+ - 每个任务有自己的队列、序列化和消费并发上限。
22
+ - `QueueFuncRuntime` 把多段任务按下游先启动、上游后停止的顺序一起管。
23
+ - `start` 会等到消费者开始收消息再返回。
24
+ - 装饰之后仍可以直接 `await` 调用,不经过队列。
25
+
26
+ ## 启动和停止
27
+
28
+ `broker.start_consumer()` 创建消费任务,并等到它开始收消息。内存队列是已经堵在 `get()` 上;RabbitMQ 是队列已经订阅上。
29
+
30
+ `stop` 先标上停止,再等手头这次收消息结束。空闲时最多再等一个 `consume_timeout`。还没取走的 RabbitMQ 消息留在代理上。
31
+
32
+ RabbitMQ 在消息交给消费池之后确认。处理函数出错不会把消息退回队列。`push` 会等发布确认,超时抛出 `queue_func.PublishTimeoutError`。
33
+
34
+ ## 安装
35
+
36
+ ```shell
37
+ pip install queue-func
38
+ ```
39
+
40
+ 使用 RabbitMQ:
41
+
42
+ ```shell
43
+ pip install "queue-func[rabbit]"
44
+ ```
45
+
46
+ 从仓库安装:
47
+
48
+ ```shell
49
+ git clone https://github.com/sekil9529/queue-func.git
50
+ cd queue-func
51
+ poetry install -E rabbit
52
+ ```
53
+
54
+ ## 用法
55
+
56
+ 从 `queue_func.factory` 引入 `queue`、`Backend`、`QueueFuncRuntime`。
57
+
58
+ ### 进程内队列
59
+
60
+ ```python
61
+ import asyncio
62
+
63
+ from queue_func.factory import Backend, queue
64
+
65
+
66
+ @queue(Backend.QUEUE, consume_timeout=0.5, maxsize=1000)
67
+ async def process_order(order_id: int) -> None:
68
+ print(order_id)
69
+
70
+
71
+ async def main() -> None:
72
+ await process_order.broker.start_consumer()
73
+ await process_order.push(args=(42,))
74
+ await process_order.broker.stop_consumer()
75
+ await process_order.broker.join_consumer()
76
+
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ `push` 就是 `broker.publish`。不传 `timeout` 时用配置里的 `publish_timeout`;传入数字只对这一次有效。`None` 表示不限时。
82
+
83
+ 直接调用:
84
+
85
+ ```python
86
+ await process_order(1)
87
+ ```
88
+
89
+ ### 多段流水线
90
+
91
+ `QueueFuncRuntime.start()` 按列表反序启动,下游先就绪。`stop()` 先逐个停消费者,再停全部生产者。
92
+
93
+ ```python
94
+ import asyncio
95
+
96
+ from queue_func.factory import Backend, QueueFuncRuntime, queue
97
+
98
+
99
+ @queue(Backend.QUEUE, consumer_pool_maxsize=4)
100
+ async def task_b(job: str) -> None:
101
+ print(job)
102
+
103
+
104
+ @queue(Backend.QUEUE, consumer_pool_maxsize=2)
105
+ async def task_a(job: str) -> None:
106
+ await task_b.push(args=(job,))
107
+
108
+
109
+ async def main() -> None:
110
+ runtime = QueueFuncRuntime([task_a, task_b])
111
+ await runtime.start()
112
+ await task_a.push(args=("job-1",))
113
+ await runtime.stop()
114
+
115
+
116
+ asyncio.run(main())
117
+ ```
118
+
119
+ 三段链式示例:
120
+
121
+ ```shell
122
+ poetry run python examples/demo_queue.py
123
+ ```
124
+
125
+ ### RabbitMQ
126
+
127
+ `url` 和 `queue_name` 必填。`enable_declare_queue=True` 时,队列不存在会创建。默认交换机名是 `""`,路由键默认用队列名。已经存在的队列再声明时,持久化等参数必须和代理上的一致。
128
+
129
+ 只投递、不消费的进程把 `enable_consumer` 设为 `False`。
130
+
131
+ ```python
132
+ import asyncio
133
+
134
+ from queue_func.factory import Backend, queue
135
+
136
+
137
+ @queue(
138
+ Backend.RABBIT_AIO_PIKA,
139
+ url="amqp://guest:guest@127.0.0.1/",
140
+ queue_name="process_order",
141
+ enable_declare_queue=True,
142
+ consume_timeout=0.5,
143
+ )
144
+ async def process_order(order_id: int) -> None:
145
+ print(order_id)
146
+
147
+
148
+ async def main() -> None:
149
+ await process_order.broker.start_consumer()
150
+ await process_order.push(args=(42,))
151
+ await process_order.broker.stop_consumer()
152
+ await process_order.broker.join_consumer()
153
+ await process_order.broker.stop_producer()
154
+ await process_order.broker.join_producer()
155
+
156
+
157
+ asyncio.run(main())
158
+ ```
159
+
160
+ 本地 RabbitMQ(`127.0.0.1:5672`,`guest` / `guest`)上的链式示例:
161
+
162
+ ```shell
163
+ poetry run python examples/demo_rabbit_aio_pika.py
164
+ ```
165
+
166
+ 常用参数:
167
+
168
+ | 参数 | 作用 |
169
+ |------|------|
170
+ | `consumer_pool_maxsize` | 同时执行的协程数,默认 200 |
171
+ | `consume_timeout` | 一次等消息的时间,默认 2 秒 |
172
+ | `publish_timeout` | 投递等待上限,默认不限 |
173
+ | `maxsize` | 仅内存队列,`asyncio.Queue` 容量,默认 200 |
174
+ | `serializer_cls` | 队列中的数据结构。内存队列默认 `raw`,RabbitMQ 默认 orjson |
175
+ | `prefetch_count` | 仅 RabbitMQ,未确认消息窗口 |
176
+ | `delivery_mode` | 仅 RabbitMQ,默认持久化 |
177
+ | `exchange_name` / `routing_key` | 仅 RabbitMQ |
178
+ | `queue_durable` | 仅 RabbitMQ,声明队列时是否持久化,默认否 |
179
+
180
+ 其余字段见 `queue_func.backends.queue.config.Config` 和 `queue_func.backends.rabbit_aio_pika.config.Config`。
181
+
182
+ ## 一条任务里有什么
183
+
184
+ `@queue` 装饰的协程就是一条队列。`push` 投到它自己这条队列上。
185
+
186
+ - **Broker** 组装生产者、消费者和双方共用的资源,并提供启停与投递。
187
+ - **Producer** 序列化后把数据写入队列。
188
+ - **Consumer** 取出数据,反序列化后交给消费池。
189
+ - **PoolExecutor** 按并发上限执行业务协程。
190
+
191
+ ```mermaid
192
+ flowchart TB
193
+ PUSH["投递"] --> B["通道"]
194
+ B --> PRO["生产者"]
195
+ B -.->|启停| CON["消费者"]
196
+
197
+ PRO -->|序列化| Q[("队列")]
198
+ Q -->|反序列化| CON
199
+ CON --> POOL["消费池"]
200
+ POOL --> FN["业务协程"]
201
+ ```
202
+
203
+ ## 测试
204
+
205
+ ```shell
206
+ poetry install -E rabbit
207
+ poetry run pytest
208
+ ```
209
+
210
+ 单元测试使用假的 RabbitMQ 连接,不要求本机有代理。`examples/demo_rabbit_aio_pika.py` 需要本机 `guest` / `guest`、端口 `5672`。
211
+
212
+ `pytest.ini` 会写出覆盖率页面 `htmlcov/` 和 `report.html`。
213
+
214
+ ## 版本
215
+
216
+ 遵循[语义化版本](https://semver.org/lang/zh-CN/)。变更记录在 [CHANGELOG.md](CHANGELOG.md)。
217
+
218
+ ## 许可证
219
+
220
+ Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "queue-func"
3
- version = "0.2.1"
3
+ version = "0.2.3"
4
4
  description = "Async coroutine functions as queue-backed tasks (QueueFunc)."
5
5
  authors = ["liuseki <liuseki@163.com>"]
6
6
  license = "Apache-2.0"
@@ -11,7 +11,7 @@ documentation = "https://github.com/sekil9529/queue-func#readme"
11
11
  packages = [{ include = "queue_func" }]
12
12
 
13
13
  [tool.poetry.urls]
14
- Changelog = "https://github.com/sekil9529/queue-func/blob/master/Changelog.rst"
14
+ Changelog = "https://github.com/sekil9529/queue-func/blob/master/CHANGELOG.md"
15
15
  Issues = "https://github.com/sekil9529/queue-func/issues"
16
16
 
17
17
  [tool.poetry.dependencies]
@@ -4,13 +4,13 @@ import asyncio
4
4
  import typing as t
5
5
 
6
6
  from queue_func.serializer.base import Message
7
- from queue_func.shared_resource.queue import Resource
8
- from queue_func.broker.base import BaseBroker as _BaseBroker
9
- from queue_func.producer.queue import Producer
10
- from queue_func.consumer.queue import Consumer
7
+ from queue_func.backends.queue.resource import Resource
8
+ from queue_func.core.broker import BaseBroker as _BaseBroker
9
+ from queue_func.backends.queue.producer import Producer
10
+ from queue_func.backends.queue.consumer import Consumer
11
11
 
12
12
  if t.TYPE_CHECKING:
13
- from queue_func.config.queue import Config
13
+ from queue_func.backends.queue.config import Config
14
14
 
15
15
 
16
16
  class Broker(_BaseBroker["Config", Message]):
@@ -6,7 +6,7 @@ from queue_func.factory.backend import Backend
6
6
  from queue_func.factory.registry import register_backend
7
7
 
8
8
  if t.TYPE_CHECKING:
9
- from queue_func.broker.base import BaseBroker
9
+ from queue_func.core.broker import BaseBroker
10
10
 
11
11
 
12
12
  @register_backend(Backend.QUEUE)
@@ -14,7 +14,7 @@ def build_queue_broker(
14
14
  coro: t.Callable[..., t.Coroutine],
15
15
  **kwargs: t.Any) -> "BaseBroker[t.Any, t.Any]":
16
16
 
17
- from queue_func.broker.queue import Broker
18
- from queue_func.config.queue import Config
17
+ from queue_func.backends.queue.broker import Broker
18
+ from queue_func.backends.queue.config import Config
19
19
 
20
20
  return Broker(Config(coro=coro, **kwargs))
@@ -3,8 +3,8 @@
3
3
  import typing as t
4
4
  from pydantic.dataclasses import dataclass as pyd_dataclass
5
5
 
6
- from queue_func.config.utils import ExtraIgnoreConfig
7
- from queue_func.config.general import Config as _Config
6
+ from queue_func.core.config import Config as _Config
7
+ from queue_func.core.config import ExtraIgnoreConfig
8
8
  from queue_func.serializer.base import BaseSerializer
9
9
  from queue_func.serializer.raw import Serializer
10
10
 
@@ -4,9 +4,9 @@ import asyncio
4
4
  import typing as t
5
5
 
6
6
  from queue_func.serializer.base import Message
7
- from queue_func.shared_resource.queue import Resource
7
+ from queue_func.backends.queue.resource import Resource
8
8
  from queue_func.pool_executor.base import BasePoolExecutor
9
- from queue_func.consumer.base import BaseConsumer as _BaseConsumer
9
+ from queue_func.core.consumer import BaseConsumer as _BaseConsumer
10
10
 
11
11
 
12
12
  class Consumer(_BaseConsumer[Message, Resource]):
@@ -14,6 +14,7 @@ class Consumer(_BaseConsumer[Message, Resource]):
14
14
  async def _consume(self) -> None:
15
15
 
16
16
  while (not self._stopped) or (not self._resource.queue.empty()):
17
+ self._mark_running()
17
18
  try:
18
19
  message: Message = await asyncio.wait_for(
19
20
  self._resource.queue.get(),
@@ -4,8 +4,8 @@ import asyncio
4
4
 
5
5
  from queue_func.errors import PublishTimeoutError
6
6
  from queue_func.serializer.base import Message
7
- from queue_func.producer.base import BaseProducer
8
- from queue_func.shared_resource.queue import Resource
7
+ from queue_func.core.producer import BaseProducer
8
+ from queue_func.backends.queue.resource import Resource
9
9
 
10
10
 
11
11
  class Producer(BaseProducer[Message, Resource]):
@@ -3,7 +3,7 @@
3
3
  import asyncio
4
4
 
5
5
  from queue_func.serializer.base import Message, BaseSerializer
6
- from queue_func.shared_resource.base import BaseResource as _Base
6
+ from queue_func.core.resource import BaseResource as _Base
7
7
 
8
8
 
9
9
  class Resource(_Base[Message]):
@@ -2,13 +2,13 @@
2
2
 
3
3
  import typing as t
4
4
 
5
- from queue_func.broker.base import BaseBroker as _BaseBroker
6
- from queue_func.consumer.rabbit_aio_pika import Consumer
7
- from queue_func.producer.rabbit_aio_pika import Producer
8
- from queue_func.shared_resource.rabbit_aio_pika import Resource
5
+ from queue_func.core.broker import BaseBroker as _BaseBroker
6
+ from queue_func.backends.rabbit_aio_pika.consumer import Consumer
7
+ from queue_func.backends.rabbit_aio_pika.producer import Producer
8
+ from queue_func.backends.rabbit_aio_pika.resource import Resource
9
9
 
10
10
  if t.TYPE_CHECKING:
11
- from queue_func.config.rabbit_aio_pika import Config
11
+ from queue_func.backends.rabbit_aio_pika.config import Config
12
12
 
13
13
 
14
14
  class Broker(_BaseBroker["Config", bytes]):
@@ -6,7 +6,7 @@ from queue_func.factory.backend import Backend
6
6
  from queue_func.factory.registry import register_backend
7
7
 
8
8
  if t.TYPE_CHECKING:
9
- from queue_func.broker.base import BaseBroker
9
+ from queue_func.core.broker import BaseBroker
10
10
 
11
11
 
12
12
  @register_backend(Backend.RABBIT_AIO_PIKA)
@@ -15,8 +15,8 @@ def build_rabbit_aio_pika_broker(
15
15
  **kwargs: t.Any) -> "BaseBroker[t.Any, t.Any]":
16
16
 
17
17
  try:
18
- from queue_func.broker.rabbit_aio_pika import Broker
19
- from queue_func.config.rabbit_aio_pika import Config
18
+ from queue_func.backends.rabbit_aio_pika.broker import Broker
19
+ from queue_func.backends.rabbit_aio_pika.config import Config
20
20
  except ImportError as exc:
21
21
  raise ImportError(
22
22
  "Backend.RABBIT_AIO_PIKA requires aio-pika. "
@@ -4,8 +4,8 @@ from enum import unique, StrEnum, IntEnum
4
4
 
5
5
  from pydantic.dataclasses import dataclass as pyd_dataclass
6
6
 
7
- from queue_func.config.utils import ExtraIgnoreConfig
8
- from queue_func.config.general import Config as _Config
7
+ from queue_func.core.config import Config as _Config
8
+ from queue_func.core.config import ExtraIgnoreConfig
9
9
 
10
10
 
11
11
  @unique
@@ -3,9 +3,9 @@
3
3
  import asyncio
4
4
  import typing as t
5
5
 
6
- from queue_func.consumer.base import BaseConsumer as _BaseConsumer
6
+ from queue_func.core.consumer import BaseConsumer as _BaseConsumer
7
7
  from queue_func.pool_executor.base import BasePoolExecutor
8
- from queue_func.shared_resource.rabbit_aio_pika import Resource
8
+ from queue_func.backends.rabbit_aio_pika.resource import Resource
9
9
 
10
10
  if t.TYPE_CHECKING:
11
11
  from aio_pika.abc import AbstractIncomingMessage, AbstractQueue
@@ -55,6 +55,7 @@ class Consumer(_BaseConsumer[bytes, Resource]):
55
55
  await self._ensure_queue()
56
56
  while not self._stopped:
57
57
  async with self._queue.iterator(timeout=self._consume_timeout) as queue_iter:
58
+ self._mark_running()
58
59
  try:
59
60
  message: "AbstractIncomingMessage"
60
61
  async for message in queue_iter:
@@ -5,10 +5,10 @@ import typing as t
5
5
 
6
6
  import aio_pika
7
7
 
8
- from queue_func.config.rabbit_aio_pika import DeliveryMode, ExchangeName
8
+ from queue_func.backends.rabbit_aio_pika.config import DeliveryMode, ExchangeName
9
9
  from queue_func.errors import PublishTimeoutError
10
- from queue_func.producer.base import BaseProducer
11
- from queue_func.shared_resource.rabbit_aio_pika import Resource
10
+ from queue_func.core.producer import BaseProducer
11
+ from queue_func.backends.rabbit_aio_pika.resource import Resource
12
12
 
13
13
  if t.TYPE_CHECKING:
14
14
  from aio_pika.abc import AbstractChannel, AbstractExchange
@@ -6,7 +6,7 @@ import typing as t
6
6
  import aio_pika
7
7
 
8
8
  from queue_func.utils.deco import before
9
- from queue_func.shared_resource.base import BaseResource as _Base
9
+ from queue_func.core.resource import BaseResource as _Base
10
10
  from queue_func.serializer.base import Message, BaseSerializer
11
11
 
12
12
  if t.TYPE_CHECKING:
@@ -4,12 +4,12 @@ import abc
4
4
  import typing as t
5
5
 
6
6
  from queue_func.serializer.base import Message
7
- from queue_func.shared_resource.base import Resource
7
+ from queue_func.core.resource import Resource
8
8
 
9
9
  if t.TYPE_CHECKING:
10
- from queue_func.config.general import Config
11
- from queue_func.producer.base import BaseProducer
12
- from queue_func.consumer.base import BaseConsumer
10
+ from queue_func.core.config import Config
11
+ from queue_func.core.producer import BaseProducer
12
+ from queue_func.core.consumer import BaseConsumer
13
13
 
14
14
 
15
15
  ConfigType = t.TypeVar("ConfigType", bound="Config")
@@ -2,15 +2,23 @@
2
2
 
3
3
  import typing as t
4
4
 
5
+ from pydantic.config import ConfigDict
5
6
  from pydantic.dataclasses import dataclass as pyd_dataclass
6
7
 
7
- from queue_func.config.utils import ExtraIgnoreConfig
8
8
  from queue_func.serializer.base import BaseSerializer
9
9
  from queue_func.serializer.orjson_ser import Serializer
10
10
  from queue_func.pool_executor.base import BasePoolExecutor
11
11
  from queue_func.pool_executor.general import PoolExecutor
12
12
 
13
13
 
14
+ ExtraIgnoreConfig = ConfigDict(
15
+ extra="ignore",
16
+ validate_assignment=True,
17
+ arbitrary_types_allowed=True,
18
+ coerce_numbers_to_str=True,
19
+ )
20
+
21
+
14
22
  @pyd_dataclass(frozen=True, kw_only=True, config=ExtraIgnoreConfig)
15
23
  class Config:
16
24
 
@@ -8,7 +8,7 @@ import typing as t
8
8
  from functools import wraps
9
9
 
10
10
  from queue_func.serializer.base import Message
11
- from queue_func.shared_resource.base import Resource
11
+ from queue_func.core.resource import Resource
12
12
  from queue_func.pool_executor.base import BasePoolExecutor
13
13
  from queue_func.utils.hook import OnceHooks
14
14
 
@@ -24,6 +24,7 @@ class BaseConsumer(t.Generic[Message, Resource], metaclass=abc.ABCMeta):
24
24
  "_startup",
25
25
  "_shutdown",
26
26
  "_stopped",
27
+ "_running",
27
28
  "_logger",
28
29
  )
29
30
 
@@ -42,6 +43,7 @@ class BaseConsumer(t.Generic[Message, Resource], metaclass=abc.ABCMeta):
42
43
  self._pool_executor = self._init_pool_executor(pool_executor_cls, pool_maxsize)
43
44
  self._consume_timeout = consume_timeout
44
45
  self._stopped = False
46
+ self._running = asyncio.Event()
45
47
  self._startup = OnceHooks()
46
48
  self._shutdown = OnceHooks()
47
49
  self._logger = logging.getLogger(__name__)
@@ -84,6 +86,12 @@ class BaseConsumer(t.Generic[Message, Resource], metaclass=abc.ABCMeta):
84
86
  self._startup.register(self._run_until_stopped(self._consume))
85
87
  self._register_shutdown()
86
88
  await self._startup.start()
89
+ await self._running.wait()
90
+
91
+ def _mark_running(self) -> None:
92
+ """消费者已经开始收消息。``consume`` 等到这里才返回。"""
93
+
94
+ self._running.set()
87
95
 
88
96
  @abc.abstractmethod
89
97
  async def _consume(self) -> None: # pragma: no cover
@@ -3,7 +3,7 @@
3
3
  import abc
4
4
  import typing as t
5
5
 
6
- from queue_func.shared_resource.base import Resource
6
+ from queue_func.core.resource import Resource
7
7
  from queue_func.serializer.base import Message
8
8
  from queue_func.utils.hook import OnceHooks
9
9
  from queue_func.utils.waiter import IdleWaiter
@@ -2,8 +2,8 @@
2
2
 
3
3
  """Import builder modules so they register with :func:`register_backend`."""
4
4
 
5
- from queue_func.factory.builders import queue as queue
6
- from queue_func.factory.builders import rabbit_aio_pika as rabbit_aio_pika
5
+ from queue_func.backends.queue import build as queue
6
+ from queue_func.backends.rabbit_aio_pika import build as rabbit_aio_pika
7
7
 
8
8
  __all__ = (
9
9
  "queue",
@@ -2,14 +2,14 @@
2
2
 
3
3
  import typing as t
4
4
 
5
- from queue_func.config.rabbit_aio_pika import DeliveryMode, ExchangeName
5
+ from queue_func.backends.rabbit_aio_pika.config import DeliveryMode, ExchangeName
6
6
  from queue_func.factory import builders as _builders # noqa: F401 — register backends
7
7
  from queue_func.factory.backend import Backend
8
8
  from queue_func.factory.registry import build_broker
9
9
 
10
10
  if t.TYPE_CHECKING:
11
- from queue_func.broker.base import BaseBroker
12
- from queue_func.config.general import Config as GeneralConfig
11
+ from queue_func.core.broker import BaseBroker
12
+ from queue_func.core.config import Config as GeneralConfig
13
13
  from queue_func.pool_executor.base import BasePoolExecutor
14
14
  from queue_func.serializer.base import BaseSerializer
15
15
 
@@ -5,7 +5,7 @@ import typing as t
5
5
  from queue_func.factory.backend import Backend
6
6
 
7
7
  if t.TYPE_CHECKING:
8
- from queue_func.broker.base import BaseBroker
8
+ from queue_func.core.broker import BaseBroker
9
9
 
10
10
  __all__ = (
11
11
  "register_backend",
@@ -1,228 +0,0 @@
1
- # queue-func
2
-
3
- 把 **`async def` 协程** 变成可 **`push`** 的队列任务:在 asyncio 里投递、消费,并按通道控制并发与背压。
4
-
5
- 仓库:[github.com/sekil9529/queue-func](https://github.com/sekil9529/queue-func)。版本记录见 [Changelog.rst](Changelog.rst)。
6
-
7
- > **需要 Python 3.12 及以上。** 任务入口必须是协程函数,普通函数不能作为任务。
8
- >
9
- > RabbitMQ 后端需要另装可选依赖,底层使用 [aio-pika](https://github.com/mosquito/aio-pika) `^9.6`。
10
-
11
- ## 特点
12
-
13
- - 完全在 asyncio 里运行。消费者在同一事件循环里按上限并发执行任务,可以和 FastAPI / Starlette 放在一个进程里。
14
- - 一条链路是序列化、投递、反序列化、执行。启停由通道或 `QueueFuncRuntime` 显式管理。
15
- - 每个 `QueueFunc` 有自己的队列和消费并发上限。`QueueFuncRuntime` 可以同时跑多段流水线,各段互不影响。
16
- - 序列化器的 `pack` / `unpack` 决定队列里的数据结构。
17
- - 通过 `register_backend` 接入新的队列后端。
18
-
19
- ## 当前后端
20
-
21
- | 后端 | 说明 |
22
- |------|------|
23
- | `Backend.QUEUE` | 进程内 `asyncio.Queue` |
24
- | `Backend.RABBIT_AIO_PIKA` | RabbitMQ,安装可选依赖 `rabbit` |
25
-
26
- 内存队列的消息留在进程里,序列化默认是 `raw`(队列中仍是 `(args, kwargs)`)。RabbitMQ 默认用 orjson,队列中是 `bytes`。
27
-
28
- ## 安装
29
-
30
- ```shell
31
- pip install queue-func
32
- ```
33
-
34
- 使用 RabbitMQ:
35
-
36
- ```shell
37
- pip install "queue-func[rabbit]"
38
- ```
39
-
40
- 从仓库安装(开发):
41
-
42
- ```shell
43
- git clone https://github.com/sekil9529/queue-func.git
44
- cd queue-func
45
- poetry install -E rabbit
46
- ```
47
-
48
- ## 用法
49
-
50
- ### 进程内队列
51
-
52
- ```python
53
- import asyncio
54
-
55
- from queue_func.factory import Backend, queue
56
-
57
-
58
- @queue(Backend.QUEUE, consume_timeout=0.5, maxsize=1000)
59
- async def process_order(order_id: int) -> None:
60
- print(order_id)
61
-
62
-
63
- async def main() -> None:
64
- await process_order.broker.start_consumer()
65
- await process_order.push(args=(42,))
66
- await process_order.broker.stop_consumer()
67
- await process_order.broker.join_consumer()
68
-
69
-
70
- asyncio.run(main())
71
- ```
72
-
73
- `push` 就是 `broker.publish`。不传 `timeout` 时用配置里的 `publish_timeout`;传入数字只作用于这一次。`None` 表示不限时。超时抛出 `queue_func.PublishTimeoutError`。
74
-
75
- 装饰之后仍可直接调用,不经过队列:
76
-
77
- ```python
78
- await process_order(1)
79
- ```
80
-
81
- ### 多段流水线
82
-
83
- `QueueFuncRuntime.start()` 按列表反序启动消费者,下游先就绪。`stop()` 先逐个停止消费者,再停止全部生产者。
84
-
85
- ```python
86
- import asyncio
87
-
88
- from queue_func.factory import Backend, QueueFuncRuntime, queue
89
-
90
-
91
- @queue(Backend.QUEUE, consumer_pool_maxsize=4)
92
- async def task_b(job: str) -> None:
93
- print(job)
94
-
95
-
96
- @queue(Backend.QUEUE, consumer_pool_maxsize=2)
97
- async def task_a(job: str) -> None:
98
- await task_b.push(args=(job,))
99
-
100
-
101
- async def main() -> None:
102
- runtime = QueueFuncRuntime([task_a, task_b])
103
- await runtime.start()
104
- await task_a.push(args=("job-1",))
105
- await runtime.stop()
106
-
107
-
108
- asyncio.run(main())
109
- ```
110
-
111
- 三段链式示例(含日志):
112
-
113
- ```shell
114
- poetry run python examples/demo_queue.py
115
- ```
116
-
117
- ### RabbitMQ
118
-
119
- `url` 和 `queue_name` 必填。`enable_declare_queue=True` 时,队列不存在会创建。默认交换机名是 `""`,路由键默认用队列名。`push` 会等待发布确认;`publish_timeout` 是这次确认的超时。
120
-
121
- 消息交给消费池之后就确认。处理函数出错不会把消息退回队列。`stop` 会设上停止标志,当前这次拉取结束(空闲时最多再等一个 `consume_timeout`)后退出;还没取走的消息留在 RabbitMQ 上。
122
-
123
- 同一个 `QueueFunc` 的生产者和消费者可以分进程运行。只投递的进程把 `enable_consumer` 设为 `False`。
124
-
125
- ```python
126
- import asyncio
127
-
128
- from queue_func.factory import Backend, queue
129
-
130
-
131
- @queue(
132
- Backend.RABBIT_AIO_PIKA,
133
- url="amqp://guest:guest@127.0.0.1/",
134
- queue_name="process_order",
135
- enable_declare_queue=True,
136
- )
137
- async def process_order(order_id: int) -> None:
138
- print(order_id)
139
-
140
-
141
- async def main() -> None:
142
- await process_order.broker.start_consumer()
143
- await process_order.push(args=(42,))
144
- await process_order.broker.stop_consumer()
145
- await process_order.broker.join_consumer()
146
- await process_order.broker.stop_producer()
147
- await process_order.broker.join_producer()
148
-
149
-
150
- asyncio.run(main())
151
- ```
152
-
153
- 本地 RabbitMQ(`127.0.0.1:5672`,`guest` / `guest`)上的链式示例:
154
-
155
- ```shell
156
- poetry run python examples/demo_rabbit_aio_pika.py
157
- ```
158
-
159
- 常用参数:
160
-
161
- | 参数 | 作用 |
162
- |------|------|
163
- | `consumer_pool_maxsize` | 同时执行的协程数,默认 200 |
164
- | `consume_timeout` | 一次拉取的等待时间,默认 2 秒 |
165
- | `publish_timeout` | 投递等待上限,默认不限 |
166
- | `maxsize` | 仅内存队列,`asyncio.Queue` 容量,默认 200 |
167
- | `serializer_cls` | 队列中的数据结构 |
168
- | `prefetch_count` | 仅 RabbitMQ,未确认消息窗口 |
169
- | `delivery_mode` | 仅 RabbitMQ,默认持久化 |
170
- | `exchange_name` / `routing_key` | 仅 RabbitMQ |
171
-
172
- 其余字段见 `queue_func.config.queue.Config` 和 `queue_func.config.rabbit_aio_pika.Config`。
173
-
174
- ## 架构
175
-
176
- 一个 `@queue` 装饰的协程就是一条队列。`push` 投到它自己这条队列上。
177
-
178
- - **Broker** 组装生产者、消费者和双方共用的资源,并提供启停与投递。
179
- - **Producer** 序列化后把数据写入队列。
180
- - **Consumer** 取出数据,反序列化后交给消费池。
181
- - **PoolExecutor** 按并发上限执行业务协程。
182
-
183
- ```mermaid
184
- flowchart TB
185
- PUSH["投递"] --> B["通道"]
186
- B --> PRO["生产者"]
187
- B -.->|启停| CON["消费者"]
188
-
189
- PRO -->|序列化| Q[("队列")]
190
- Q -->|反序列化| CON
191
- CON --> POOL["消费池"]
192
- POOL --> FN["业务协程"]
193
- ```
194
-
195
- ## 项目结构
196
-
197
- ```
198
- queue_func/
199
- ├── broker/
200
- ├── producer/
201
- ├── consumer/
202
- ├── serializer/ # raw、orjson
203
- ├── shared_resource/
204
- ├── config/
205
- ├── factory/ # @queue、QueueFunc、QueueFuncRuntime
206
- ├── pool_executor/
207
- └── utils/
208
- examples/
209
- ├── demo_queue.py
210
- └── demo_rabbit_aio_pika.py
211
- ```
212
-
213
- ## 开发与测试
214
-
215
- ```shell
216
- poetry install -E rabbit
217
- pytest
218
- ```
219
-
220
- `pytest.ini` 会生成覆盖率页面(`htmlcov/`)和 `report.html`。
221
-
222
- ## 版本
223
-
224
- 遵循[语义化版本](https://semver.org/lang/zh-CN/)。变更记录在 [Changelog.rst](Changelog.rst)。
225
-
226
- ## 许可证
227
-
228
- Apache-2.0
@@ -1,15 +0,0 @@
1
- # coding: utf-8
2
-
3
- from pydantic.config import ConfigDict
4
-
5
- __all__ = (
6
- "ExtraIgnoreConfig",
7
- )
8
-
9
-
10
- ExtraIgnoreConfig = ConfigDict(
11
- extra="ignore",
12
- validate_assignment=True,
13
- arbitrary_types_allowed=True,
14
- coerce_numbers_to_str=True,
15
- )
@@ -1 +0,0 @@
1
- # coding: utf-8
File without changes