async-task-kit 0.1.13__tar.gz → 0.1.15__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.
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/PKG-INFO +5 -1
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/README.md +4 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/consumer/coroutine.py +6 -3
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/consumer/process.py +1 -1
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/consumer/thread.py +1 -1
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/core/rabbitmq.py +16 -4
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/utils/logger.py +19 -12
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit.egg-info/PKG-INFO +5 -1
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/pyproject.toml +1 -1
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/LICENSE +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/__init__.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/consumer/__init__.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/consumer/base.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/core/__init__.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/core/processor.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/utils/__init__.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit/utils/env_loader.py +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit.egg-info/SOURCES.txt +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit.egg-info/dependency_links.txt +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit.egg-info/requires.txt +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit.egg-info/top_level.txt +0 -0
- {async_task_kit-0.1.13 → async_task_kit-0.1.15}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-task-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.15
|
|
4
4
|
Summary: A powerful async task processing kit based on RabbitMQ with Coroutine, Thread, and Process support.
|
|
5
5
|
Author-email: realwrtoff <realwrtoff@gmail.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -223,6 +223,10 @@ await rmq.pop("my_queue", durable=False)
|
|
|
223
223
|
|
|
224
224
|
`queue_length` 使用 passive 模式,无需传 `durable`。
|
|
225
225
|
|
|
226
|
+
## Changelog
|
|
227
|
+
|
|
228
|
+
各版本变更说明见 [CHANGELOG.md](CHANGELOG.md)。
|
|
229
|
+
|
|
226
230
|
## License
|
|
227
231
|
|
|
228
232
|
MIT
|
|
@@ -18,6 +18,7 @@ class CoroutineConsumer(BaseConsumer):
|
|
|
18
18
|
concurrency: int = 1,
|
|
19
19
|
max_retry: int = 3,
|
|
20
20
|
retry_delay: int = 30,
|
|
21
|
+
rmq: RabbitMQ | None = None,
|
|
21
22
|
):
|
|
22
23
|
self.amqp_url = amqp_url
|
|
23
24
|
self.queue_name = queue_name
|
|
@@ -26,7 +27,8 @@ class CoroutineConsumer(BaseConsumer):
|
|
|
26
27
|
self.max_retry = max_retry
|
|
27
28
|
self.retry_delay = retry_delay
|
|
28
29
|
|
|
29
|
-
self.
|
|
30
|
+
self._owns_rmq = rmq is None
|
|
31
|
+
self._rmq = rmq or RabbitMQ(amqp_url)
|
|
30
32
|
self._stop_event = asyncio.Event()
|
|
31
33
|
|
|
32
34
|
async def start(self):
|
|
@@ -93,5 +95,6 @@ class CoroutineConsumer(BaseConsumer):
|
|
|
93
95
|
|
|
94
96
|
async def stop(self):
|
|
95
97
|
self._stop_event.set()
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
if self._owns_rmq:
|
|
99
|
+
await self._rmq.close()
|
|
100
|
+
logger.info(f"[CoroutineConsumer] stopped | queue={self.queue_name}")
|
|
@@ -12,6 +12,7 @@ from aio_pika.exceptions import (
|
|
|
12
12
|
ChannelClosed,
|
|
13
13
|
AMQPError
|
|
14
14
|
)
|
|
15
|
+
from aiormq.exceptions import ChannelInvalidStateError
|
|
15
16
|
from aio_pika.pool import Pool
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
@@ -63,6 +64,7 @@ class RabbitMQ(BasicQueue):
|
|
|
63
64
|
channel_size: int = DEFAULT_CHANNEL_SIZE,
|
|
64
65
|
heartbeat: int = 60,
|
|
65
66
|
max_retry_count: int = DEFAULT_MAX_RETRY_COUNT,
|
|
67
|
+
|
|
66
68
|
):
|
|
67
69
|
self.rabbit_url = rabbit_url
|
|
68
70
|
self.pool_size = pool_size
|
|
@@ -157,6 +159,10 @@ class RabbitMQ(BasicQueue):
|
|
|
157
159
|
logger.warning("[PUSH] 连接断开,准备重连")
|
|
158
160
|
await self._invalidate_pools()
|
|
159
161
|
return None
|
|
162
|
+
except ChannelInvalidStateError:
|
|
163
|
+
logger.warning("[PUSH] 信道 RPC 异常,准备重建 pool")
|
|
164
|
+
await self._invalidate_pools()
|
|
165
|
+
return None
|
|
160
166
|
except Exception as e:
|
|
161
167
|
logger.error(f"[PUSH] 异常 queue={queue_name}: {e}", exc_info=True)
|
|
162
168
|
return None
|
|
@@ -172,13 +178,15 @@ class RabbitMQ(BasicQueue):
|
|
|
172
178
|
try:
|
|
173
179
|
async with self._channel_pool.acquire() as channel:
|
|
174
180
|
queue = await channel.declare_queue(queue_name, durable=durable)
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
# 使用 aio_pika 自带 timeout,勿用 asyncio.wait_for 取消 get(会关闭 channel)
|
|
182
|
+
return await queue.get(
|
|
183
|
+
timeout=self.DEFAULT_POP_TIMEOUT,
|
|
184
|
+
fail=False,
|
|
177
185
|
)
|
|
178
186
|
except (QueueEmpty, asyncio.TimeoutError):
|
|
179
187
|
return None
|
|
180
|
-
except (ConnectionClosed, ChannelClosed):
|
|
181
|
-
logger.warning("[POP]
|
|
188
|
+
except (ConnectionClosed, ChannelClosed, ChannelInvalidStateError):
|
|
189
|
+
logger.warning("[POP] 连接/信道异常,准备重建 pool")
|
|
182
190
|
await self._invalidate_pools()
|
|
183
191
|
return None
|
|
184
192
|
except Exception as e:
|
|
@@ -201,6 +209,10 @@ class RabbitMQ(BasicQueue):
|
|
|
201
209
|
logger.warning("[QUEUE_LENGTH] 连接断开,准备重连")
|
|
202
210
|
await self._invalidate_pools()
|
|
203
211
|
return self.QUEUE_LENGTH_UNAVAILABLE
|
|
212
|
+
except ChannelInvalidStateError:
|
|
213
|
+
logger.warning("[QUEUE_LENGTH] 信道 RPC 异常,准备重建 pool")
|
|
214
|
+
await self._invalidate_pools()
|
|
215
|
+
return self.QUEUE_LENGTH_UNAVAILABLE
|
|
204
216
|
except Exception as e:
|
|
205
217
|
logger.error(f"[QUEUE_LENGTH] 异常 queue={queue_name}: {e}", exc_info=True)
|
|
206
218
|
return self.QUEUE_LENGTH_UNAVAILABLE
|
|
@@ -2,34 +2,41 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
from logging.handlers import TimedRotatingFileHandler
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def setup_logger(
|
|
6
|
-
name: str =
|
|
7
|
+
name: str | None = None,
|
|
7
8
|
log_dir: str = "logs",
|
|
8
|
-
backup_count: int = 7 * 24 # 7天 * 24小时
|
|
9
|
-
|
|
9
|
+
backup_count: int = 7 * 24, # 7天 * 24小时
|
|
10
|
+
level: int = logging.INFO,
|
|
11
|
+
) -> logging.Logger:
|
|
12
|
+
"""Configure logging handlers.
|
|
13
|
+
|
|
14
|
+
By default configures the **root** logger so ``logging.getLogger(__name__)``
|
|
15
|
+
works everywhere. Pass ``name="app"`` to configure a named logger only.
|
|
16
|
+
"""
|
|
10
17
|
os.makedirs(log_dir, exist_ok=True)
|
|
11
18
|
logger = logging.getLogger(name)
|
|
12
|
-
logger.setLevel(
|
|
19
|
+
logger.setLevel(level)
|
|
13
20
|
logger.handlers.clear()
|
|
21
|
+
logger.propagate = name is not None
|
|
22
|
+
|
|
23
|
+
formatter = logging.Formatter(
|
|
24
|
+
"%(asctime)s | %(levelname)-8s | %(name)s | %(message)s"
|
|
25
|
+
)
|
|
14
26
|
|
|
15
|
-
# 按小时切割,保留7天
|
|
16
27
|
file_handler = TimedRotatingFileHandler(
|
|
17
28
|
when="H",
|
|
18
29
|
interval=1,
|
|
19
30
|
backupCount=backup_count,
|
|
20
31
|
encoding="utf-8",
|
|
21
|
-
filename=f"{log_dir}/app.log"
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
# 可观测格式:结构化纯文本(对接日志平台最舒服)
|
|
25
|
-
formatter = logging.Formatter(
|
|
26
|
-
"%(asctime)s | %(levelname)-8s | %(name)s | %(message)s"
|
|
32
|
+
filename=f"{log_dir}/app.log",
|
|
27
33
|
)
|
|
28
34
|
file_handler.setFormatter(formatter)
|
|
35
|
+
file_handler.setLevel(level)
|
|
29
36
|
|
|
30
|
-
# 控制台输出
|
|
31
37
|
console = logging.StreamHandler()
|
|
32
38
|
console.setFormatter(formatter)
|
|
39
|
+
console.setLevel(level)
|
|
33
40
|
|
|
34
41
|
logger.addHandler(file_handler)
|
|
35
42
|
logger.addHandler(console)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: async-task-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.15
|
|
4
4
|
Summary: A powerful async task processing kit based on RabbitMQ with Coroutine, Thread, and Process support.
|
|
5
5
|
Author-email: realwrtoff <realwrtoff@gmail.com>
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -223,6 +223,10 @@ await rmq.pop("my_queue", durable=False)
|
|
|
223
223
|
|
|
224
224
|
`queue_length` 使用 passive 模式,无需传 `durable`。
|
|
225
225
|
|
|
226
|
+
## Changelog
|
|
227
|
+
|
|
228
|
+
各版本变更说明见 [CHANGELOG.md](CHANGELOG.md)。
|
|
229
|
+
|
|
226
230
|
## License
|
|
227
231
|
|
|
228
232
|
MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "async-task-kit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.15"
|
|
8
8
|
description = "A powerful async task processing kit based on RabbitMQ with Coroutine, Thread, and Process support."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_task_kit-0.1.13 → async_task_kit-0.1.15}/async_task_kit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|