aury-boot 0.0.29__py3-none-any.whl → 0.0.31__py3-none-any.whl
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.
- aury/boot/_version.py +2 -2
- aury/boot/application/__init__.py +2 -4
- aury/boot/application/app/base.py +126 -2
- aury/boot/application/app/components.py +226 -1
- aury/boot/application/config/settings.py +201 -3
- aury/boot/application/constants/components.py +3 -0
- aury/boot/application/middleware/logging.py +45 -6
- aury/boot/commands/docs.py +40 -0
- aury/boot/commands/init.py +2 -0
- aury/boot/commands/templates/project/AGENTS.md.tpl +59 -0
- aury/boot/commands/templates/project/alert_rules.example.yaml.tpl +85 -0
- aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +3 -0
- aury/boot/commands/templates/project/aury_docs/17-alerting.md.tpl +210 -0
- aury/boot/commands/templates/project/env_templates/messaging.tpl +21 -13
- aury/boot/commands/templates/project/env_templates/monitoring.tpl +63 -0
- aury/boot/common/logging/context.py +17 -1
- aury/boot/common/logging/format.py +4 -0
- aury/boot/infrastructure/__init__.py +4 -8
- aury/boot/infrastructure/channel/__init__.py +9 -8
- aury/boot/infrastructure/channel/backends/__init__.py +2 -6
- aury/boot/infrastructure/channel/backends/broadcaster.py +141 -0
- aury/boot/infrastructure/channel/base.py +11 -4
- aury/boot/infrastructure/channel/manager.py +25 -24
- aury/boot/infrastructure/database/query_tools/__init__.py +3 -5
- aury/boot/infrastructure/events/__init__.py +4 -6
- aury/boot/infrastructure/events/backends/__init__.py +2 -4
- aury/boot/infrastructure/events/backends/broadcaster.py +189 -0
- aury/boot/infrastructure/events/base.py +9 -4
- aury/boot/infrastructure/events/manager.py +24 -20
- aury/boot/infrastructure/monitoring/__init__.py +210 -6
- aury/boot/infrastructure/monitoring/alerting/__init__.py +50 -0
- aury/boot/infrastructure/monitoring/alerting/aggregator.py +193 -0
- aury/boot/infrastructure/monitoring/alerting/events.py +141 -0
- aury/boot/infrastructure/monitoring/alerting/manager.py +430 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/__init__.py +16 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/base.py +60 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/feishu.py +209 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/webhook.py +110 -0
- aury/boot/infrastructure/monitoring/alerting/rules.py +179 -0
- aury/boot/infrastructure/monitoring/health/__init__.py +231 -0
- aury/boot/infrastructure/monitoring/tracing/__init__.py +55 -0
- aury/boot/infrastructure/monitoring/tracing/context.py +43 -0
- aury/boot/infrastructure/monitoring/tracing/logging.py +73 -0
- aury/boot/infrastructure/monitoring/tracing/processor.py +357 -0
- aury/boot/infrastructure/monitoring/tracing/provider.py +322 -0
- aury/boot/infrastructure/monitoring/tracing/tracing.py +235 -0
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.31.dist-info}/METADATA +14 -1
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.31.dist-info}/RECORD +50 -33
- aury/boot/infrastructure/channel/backends/memory.py +0 -126
- aury/boot/infrastructure/channel/backends/redis.py +0 -130
- aury/boot/infrastructure/events/backends/memory.py +0 -86
- aury/boot/infrastructure/events/backends/redis.py +0 -169
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.31.dist-info}/WHEEL +0 -0
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.31.dist-info}/entry_points.txt +0 -0
|
@@ -9,8 +9,7 @@ from collections.abc import AsyncIterator
|
|
|
9
9
|
|
|
10
10
|
from aury.boot.common.logging import logger
|
|
11
11
|
|
|
12
|
-
from .backends.
|
|
13
|
-
from .backends.redis import RedisChannel
|
|
12
|
+
from .backends.broadcaster import BroadcasterChannel
|
|
14
13
|
from .base import ChannelBackend, ChannelMessage, IChannel
|
|
15
14
|
|
|
16
15
|
|
|
@@ -51,7 +50,6 @@ class ChannelManager:
|
|
|
51
50
|
self._backend: IChannel | None = None
|
|
52
51
|
self._backend_type: ChannelBackend | None = None
|
|
53
52
|
self._initialized: bool = False
|
|
54
|
-
self._redis_client = None
|
|
55
53
|
self._url: str | None = None
|
|
56
54
|
|
|
57
55
|
@classmethod
|
|
@@ -84,17 +82,19 @@ class ChannelManager:
|
|
|
84
82
|
|
|
85
83
|
async def initialize(
|
|
86
84
|
self,
|
|
87
|
-
backend: ChannelBackend | str = ChannelBackend.
|
|
85
|
+
backend: ChannelBackend | str = ChannelBackend.BROADCASTER,
|
|
88
86
|
*,
|
|
89
|
-
url: str
|
|
90
|
-
max_subscribers: int = 1000,
|
|
87
|
+
url: str = "memory://",
|
|
91
88
|
) -> ChannelManager:
|
|
92
89
|
"""初始化通道(链式调用)。
|
|
93
90
|
|
|
94
91
|
Args:
|
|
95
|
-
backend:
|
|
96
|
-
url:
|
|
97
|
-
|
|
92
|
+
backend: 后端类型,默认 broadcaster
|
|
93
|
+
url: 连接 URL,支持:
|
|
94
|
+
- memory:// - 内存后端(单进程,默认)
|
|
95
|
+
- redis://host:port/db - Redis Pub/Sub
|
|
96
|
+
- kafka://host:port - Apache Kafka
|
|
97
|
+
- postgres://user:pass@host/db - PostgreSQL
|
|
98
98
|
|
|
99
99
|
Returns:
|
|
100
100
|
self: 支持链式调用
|
|
@@ -110,24 +110,28 @@ class ChannelManager:
|
|
|
110
110
|
self._backend_type = backend
|
|
111
111
|
self._url = url
|
|
112
112
|
|
|
113
|
-
if backend == ChannelBackend.
|
|
114
|
-
self._backend =
|
|
115
|
-
elif backend
|
|
116
|
-
|
|
117
|
-
raise ValueError("Redis 通道需要提供 url 参数")
|
|
118
|
-
# 内部创建 RedisClient
|
|
119
|
-
from aury.boot.infrastructure.clients.redis import RedisClient
|
|
120
|
-
|
|
121
|
-
self._redis_client = RedisClient()
|
|
122
|
-
await self._redis_client.configure(url=url).initialize()
|
|
123
|
-
self._backend = RedisChannel(self._redis_client)
|
|
113
|
+
if backend == ChannelBackend.BROADCASTER:
|
|
114
|
+
self._backend = BroadcasterChannel(url)
|
|
115
|
+
elif backend in (ChannelBackend.RABBITMQ, ChannelBackend.ROCKETMQ):
|
|
116
|
+
raise NotImplementedError(f"{backend.value} 后端暂未实现")
|
|
124
117
|
else:
|
|
125
118
|
raise ValueError(f"不支持的通道后端: {backend}")
|
|
126
119
|
|
|
127
120
|
self._initialized = True
|
|
128
|
-
logger.info(f"通道管理器 [{self.name}] 初始化完成: {backend.value}")
|
|
121
|
+
logger.info(f"通道管理器 [{self.name}] 初始化完成: {backend.value}, url={self._mask_url(url)}")
|
|
129
122
|
return self
|
|
130
123
|
|
|
124
|
+
def _mask_url(self, url: str) -> str:
|
|
125
|
+
"""URL 脱敏(隐藏密码)。"""
|
|
126
|
+
if "@" in url:
|
|
127
|
+
parts = url.split("@")
|
|
128
|
+
prefix = parts[0]
|
|
129
|
+
suffix = parts[1]
|
|
130
|
+
if ":" in prefix:
|
|
131
|
+
scheme_and_user = prefix.rsplit(":", 1)[0]
|
|
132
|
+
return f"{scheme_and_user}:***@{suffix}"
|
|
133
|
+
return url
|
|
134
|
+
|
|
131
135
|
@property
|
|
132
136
|
def backend(self) -> IChannel:
|
|
133
137
|
"""获取通道后端。"""
|
|
@@ -218,9 +222,6 @@ class ChannelManager:
|
|
|
218
222
|
if self._backend:
|
|
219
223
|
await self._backend.close()
|
|
220
224
|
self._backend = None
|
|
221
|
-
if self._redis_client:
|
|
222
|
-
await self._redis_client.cleanup()
|
|
223
|
-
self._redis_client = None
|
|
224
225
|
self._initialized = False
|
|
225
226
|
logger.info(f"通道管理器 [{self.name}] 已关闭")
|
|
226
227
|
|
|
@@ -12,8 +12,8 @@ import time
|
|
|
12
12
|
|
|
13
13
|
from aury.boot.common.logging import logger
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
|
|
15
|
+
# 默认慢查询阈值(秒)
|
|
16
|
+
DEFAULT_SLOW_QUERY_THRESHOLD = 1.0
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def cache_query(
|
|
@@ -86,7 +86,7 @@ def cache_query(
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
def monitor_query(
|
|
89
|
-
slow_threshold: float =
|
|
89
|
+
slow_threshold: float = DEFAULT_SLOW_QUERY_THRESHOLD,
|
|
90
90
|
enable_explain: bool = False,
|
|
91
91
|
) -> Callable:
|
|
92
92
|
"""查询性能监控装饰器。
|
|
@@ -104,7 +104,6 @@ def monitor_query(
|
|
|
104
104
|
async def list(self, **filters):
|
|
105
105
|
return await super().list(**filters)
|
|
106
106
|
"""
|
|
107
|
-
|
|
108
107
|
def decorator(func: Callable) -> Callable:
|
|
109
108
|
@wraps(func)
|
|
110
109
|
async def wrapper(self, *args, **kwargs):
|
|
@@ -154,7 +153,6 @@ def monitor_query(
|
|
|
154
153
|
|
|
155
154
|
return decorator
|
|
156
155
|
|
|
157
|
-
|
|
158
156
|
__all__ = [
|
|
159
157
|
"cache_query",
|
|
160
158
|
"monitor_query",
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
提供发布/订阅模式的事件总线功能,用于模块间解耦通信。
|
|
4
4
|
|
|
5
5
|
支持的后端:
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
- rabbitmq: RabbitMQ Exchange(分布式)
|
|
6
|
+
- broadcaster: 基于 broadcaster 库(推荐,支持 memory/redis/kafka/postgres)
|
|
7
|
+
- rabbitmq: RabbitMQ Exchange(复杂消息场景)
|
|
9
8
|
"""
|
|
10
9
|
|
|
11
|
-
from .backends import
|
|
10
|
+
from .backends import BroadcasterEventBus, RabbitMQEventBus
|
|
12
11
|
from .base import Event, EventBackend, EventHandler, EventType, IEventBus
|
|
13
12
|
from .manager import EventBusManager
|
|
14
13
|
|
|
@@ -22,9 +21,8 @@ __all__ = [
|
|
|
22
21
|
"EventType",
|
|
23
22
|
"IEventBus",
|
|
24
23
|
# 后端实现
|
|
25
|
-
"
|
|
24
|
+
"BroadcasterEventBus",
|
|
26
25
|
"RabbitMQEventBus",
|
|
27
|
-
"RedisEventBus",
|
|
28
26
|
]
|
|
29
27
|
|
|
30
28
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"""事件总线后端实现。"""
|
|
2
2
|
|
|
3
|
-
from .
|
|
3
|
+
from .broadcaster import BroadcasterEventBus
|
|
4
4
|
from .rabbitmq import RabbitMQEventBus
|
|
5
|
-
from .redis import RedisEventBus
|
|
6
5
|
|
|
7
6
|
__all__ = [
|
|
8
|
-
"
|
|
7
|
+
"BroadcasterEventBus",
|
|
9
8
|
"RabbitMQEventBus",
|
|
10
|
-
"RedisEventBus",
|
|
11
9
|
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Broadcaster 事件总线后端。
|
|
2
|
+
|
|
3
|
+
使用 broadcaster 库实现事件发布/订阅,支持多种后端:
|
|
4
|
+
- memory:// 内存(单进程)
|
|
5
|
+
- redis:// Redis Pub/Sub(多进程/多实例)
|
|
6
|
+
- kafka:// Apache Kafka
|
|
7
|
+
- postgres:// PostgreSQL LISTEN/NOTIFY
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
|
|
15
|
+
from broadcaster import Broadcast
|
|
16
|
+
|
|
17
|
+
from aury.boot.common.logging import logger
|
|
18
|
+
|
|
19
|
+
from ..base import Event, EventHandler, IEventBus
|
|
20
|
+
|
|
21
|
+
# 框架默认前缀
|
|
22
|
+
DEFAULT_CHANNEL_PREFIX = "aury:event:"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class BroadcasterEventBus(IEventBus):
|
|
26
|
+
"""Broadcaster 事件总线实现。
|
|
27
|
+
|
|
28
|
+
使用 broadcaster 库实现事件发布/订阅。
|
|
29
|
+
|
|
30
|
+
频道命名格式:{channel_prefix}{event_name}
|
|
31
|
+
默认:aury:event:user.created
|
|
32
|
+
|
|
33
|
+
优点:
|
|
34
|
+
- 统一接口支持多种后端
|
|
35
|
+
- 内置连接池管理
|
|
36
|
+
- 自动重连机制
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
url: str,
|
|
42
|
+
*,
|
|
43
|
+
channel_prefix: str = DEFAULT_CHANNEL_PREFIX,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""初始化 Broadcaster 事件总线。
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
url: 连接 URL,格式:
|
|
49
|
+
- memory:// 内存(单进程)
|
|
50
|
+
- redis://host:port Redis Pub/Sub
|
|
51
|
+
- kafka://host:port Apache Kafka
|
|
52
|
+
- postgres://... PostgreSQL
|
|
53
|
+
channel_prefix: 频道名称前缀,默认 "aury:event:"
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
self._url = url
|
|
57
|
+
self._channel_prefix = channel_prefix
|
|
58
|
+
self._broadcast: Broadcast | None = None
|
|
59
|
+
# event_name -> list of handlers (本地订阅)
|
|
60
|
+
self._handlers: dict[str, list[EventHandler]] = {}
|
|
61
|
+
self._listener_tasks: dict[str, asyncio.Task] = {}
|
|
62
|
+
self._running = False
|
|
63
|
+
|
|
64
|
+
async def _ensure_connected(self) -> None:
|
|
65
|
+
"""确保已连接。"""
|
|
66
|
+
if self._broadcast is None:
|
|
67
|
+
self._broadcast = Broadcast(self._url)
|
|
68
|
+
await self._broadcast.connect()
|
|
69
|
+
logger.debug(f"Broadcaster 事件总线已连接: {self._url}")
|
|
70
|
+
|
|
71
|
+
def _get_event_name(self, event_type: type[Event] | str) -> str:
|
|
72
|
+
"""获取事件名称。"""
|
|
73
|
+
if isinstance(event_type, str):
|
|
74
|
+
return event_type
|
|
75
|
+
return event_type.__name__
|
|
76
|
+
|
|
77
|
+
def _get_channel(self, event_name: str) -> str:
|
|
78
|
+
"""获取频道名称。"""
|
|
79
|
+
return f"{self._channel_prefix}{event_name}"
|
|
80
|
+
|
|
81
|
+
def subscribe(
|
|
82
|
+
self,
|
|
83
|
+
event_type: type[Event] | str,
|
|
84
|
+
handler: EventHandler,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""订阅事件。
|
|
87
|
+
|
|
88
|
+
注意:这是同步注册 handler,真正的监听在 start_listening() 中启动。
|
|
89
|
+
"""
|
|
90
|
+
event_name = self._get_event_name(event_type)
|
|
91
|
+
if event_name not in self._handlers:
|
|
92
|
+
self._handlers[event_name] = []
|
|
93
|
+
if handler not in self._handlers[event_name]:
|
|
94
|
+
self._handlers[event_name].append(handler)
|
|
95
|
+
logger.debug(f"订阅事件: {event_name} -> {handler.__name__}")
|
|
96
|
+
|
|
97
|
+
# 如果已经在运行,立即为新事件启动监听
|
|
98
|
+
if self._running and event_name not in self._listener_tasks:
|
|
99
|
+
task = asyncio.create_task(self._listen_event(event_name))
|
|
100
|
+
self._listener_tasks[event_name] = task
|
|
101
|
+
|
|
102
|
+
def unsubscribe(
|
|
103
|
+
self,
|
|
104
|
+
event_type: type[Event] | str,
|
|
105
|
+
handler: EventHandler,
|
|
106
|
+
) -> None:
|
|
107
|
+
"""取消订阅事件。"""
|
|
108
|
+
event_name = self._get_event_name(event_type)
|
|
109
|
+
if event_name in self._handlers:
|
|
110
|
+
try:
|
|
111
|
+
self._handlers[event_name].remove(handler)
|
|
112
|
+
logger.debug(f"取消订阅事件: {event_name} -> {handler.__name__}")
|
|
113
|
+
|
|
114
|
+
# 如果该事件没有处理器了,停止监听
|
|
115
|
+
if not self._handlers[event_name] and event_name in self._listener_tasks:
|
|
116
|
+
self._listener_tasks[event_name].cancel()
|
|
117
|
+
del self._listener_tasks[event_name]
|
|
118
|
+
except ValueError:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
async def publish(self, event: Event) -> None:
|
|
122
|
+
"""发布事件。"""
|
|
123
|
+
await self._ensure_connected()
|
|
124
|
+
event_name = event.event_name
|
|
125
|
+
channel = self._get_channel(event_name)
|
|
126
|
+
data = json.dumps(event.to_dict())
|
|
127
|
+
await self._broadcast.publish(channel=channel, message=data)
|
|
128
|
+
|
|
129
|
+
async def _listen_event(self, event_name: str) -> None:
|
|
130
|
+
"""监听单个事件的消息。"""
|
|
131
|
+
channel = self._get_channel(event_name)
|
|
132
|
+
try:
|
|
133
|
+
async with self._broadcast.subscribe(channel=channel) as subscriber:
|
|
134
|
+
async for event_data in subscriber:
|
|
135
|
+
if not self._running:
|
|
136
|
+
break
|
|
137
|
+
try:
|
|
138
|
+
data = json.loads(event_data.message)
|
|
139
|
+
handlers = self._handlers.get(event_name, [])
|
|
140
|
+
for handler in handlers:
|
|
141
|
+
try:
|
|
142
|
+
event = Event.from_dict(data)
|
|
143
|
+
result = handler(event)
|
|
144
|
+
if asyncio.iscoroutine(result):
|
|
145
|
+
await result
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.error(f"处理事件 {event_name} 失败: {e}")
|
|
148
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
149
|
+
logger.warning(f"解析事件消息失败: {e}")
|
|
150
|
+
except asyncio.CancelledError:
|
|
151
|
+
pass
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.error(f"事件监听异常 {event_name}: {e}")
|
|
154
|
+
|
|
155
|
+
async def start_listening(self) -> None:
|
|
156
|
+
"""开始监听事件(需要在后台任务中运行)。"""
|
|
157
|
+
if self._running:
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
await self._ensure_connected()
|
|
161
|
+
self._running = True
|
|
162
|
+
|
|
163
|
+
# 为每个已订阅的事件启动监听任务
|
|
164
|
+
for event_name in self._handlers:
|
|
165
|
+
if event_name not in self._listener_tasks:
|
|
166
|
+
task = asyncio.create_task(self._listen_event(event_name))
|
|
167
|
+
self._listener_tasks[event_name] = task
|
|
168
|
+
|
|
169
|
+
logger.debug(f"Broadcaster 事件总线开始监听,事件数: {len(self._handlers)}")
|
|
170
|
+
|
|
171
|
+
async def close(self) -> None:
|
|
172
|
+
"""关闭事件总线。"""
|
|
173
|
+
self._running = False
|
|
174
|
+
|
|
175
|
+
# 取消所有监听任务
|
|
176
|
+
for task in self._listener_tasks.values():
|
|
177
|
+
task.cancel()
|
|
178
|
+
self._listener_tasks.clear()
|
|
179
|
+
|
|
180
|
+
# 关闭连接
|
|
181
|
+
if self._broadcast:
|
|
182
|
+
await self._broadcast.disconnect()
|
|
183
|
+
self._broadcast = None
|
|
184
|
+
|
|
185
|
+
self._handlers.clear()
|
|
186
|
+
logger.debug("Broadcaster 事件总线已关闭")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
__all__ = ["BroadcasterEventBus"]
|
|
@@ -15,11 +15,16 @@ import uuid
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class EventBackend(Enum):
|
|
18
|
-
"""事件总线后端类型。
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
"""事件总线后端类型。
|
|
19
|
+
|
|
20
|
+
- BROADCASTER: 基于 broadcaster 库,支持 memory/redis/kafka/postgres
|
|
21
|
+
- RABBITMQ: 专用 RabbitMQ 实现(复杂消息场景)
|
|
22
|
+
- ROCKETMQ: 专用 RocketMQ 实现(预留)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
BROADCASTER = "broadcaster"
|
|
22
26
|
RABBITMQ = "rabbitmq"
|
|
27
|
+
ROCKETMQ = "rocketmq"
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
@dataclass
|
|
@@ -10,14 +10,12 @@ from typing import TYPE_CHECKING, Any
|
|
|
10
10
|
|
|
11
11
|
from aury.boot.common.logging import logger
|
|
12
12
|
|
|
13
|
-
from .backends.
|
|
13
|
+
from .backends.broadcaster import BroadcasterEventBus
|
|
14
14
|
from .backends.rabbitmq import RabbitMQEventBus
|
|
15
|
-
from .backends.redis import RedisEventBus
|
|
16
15
|
from .base import Event, EventBackend, EventHandler, IEventBus
|
|
17
16
|
|
|
18
17
|
if TYPE_CHECKING:
|
|
19
18
|
from aury.boot.application.config import EventInstanceConfig
|
|
20
|
-
from aury.boot.infrastructure.clients.redis import RedisClient
|
|
21
19
|
|
|
22
20
|
|
|
23
21
|
class EventBusManager:
|
|
@@ -25,19 +23,19 @@ class EventBusManager:
|
|
|
25
23
|
|
|
26
24
|
提供统一的事件总线管理接口,支持:
|
|
27
25
|
- 多实例管理(如 local、distributed 各自独立)
|
|
28
|
-
- 多后端支持(
|
|
26
|
+
- 多后端支持(broadcaster、rabbitmq)
|
|
29
27
|
- 发布/订阅模式
|
|
30
28
|
|
|
31
29
|
使用示例:
|
|
32
30
|
# 默认实例(内存)
|
|
33
31
|
events = EventBusManager.get_instance()
|
|
34
|
-
await events.initialize(backend="memory")
|
|
32
|
+
await events.initialize(backend="broadcaster", url="memory://")
|
|
35
33
|
|
|
36
|
-
#
|
|
34
|
+
# 分布式实例(Redis)
|
|
37
35
|
distributed = EventBusManager.get_instance("distributed")
|
|
38
36
|
await distributed.initialize(
|
|
39
|
-
backend="
|
|
40
|
-
|
|
37
|
+
backend="broadcaster",
|
|
38
|
+
url="redis://localhost:6379/2",
|
|
41
39
|
)
|
|
42
40
|
|
|
43
41
|
# 订阅事件
|
|
@@ -92,10 +90,9 @@ class EventBusManager:
|
|
|
92
90
|
|
|
93
91
|
async def initialize(
|
|
94
92
|
self,
|
|
95
|
-
backend: EventBackend | str = EventBackend.
|
|
93
|
+
backend: EventBackend | str = EventBackend.BROADCASTER,
|
|
96
94
|
*,
|
|
97
95
|
config: EventInstanceConfig | None = None,
|
|
98
|
-
redis_client: RedisClient | None = None,
|
|
99
96
|
url: str | None = None,
|
|
100
97
|
channel_prefix: str | None = None,
|
|
101
98
|
exchange_name: str = "aury.events",
|
|
@@ -105,9 +102,13 @@ class EventBusManager:
|
|
|
105
102
|
Args:
|
|
106
103
|
backend: 后端类型(当 config 不为 None 时忽略)
|
|
107
104
|
config: Event 实例配置(推荐,自动根据 backend 初始化)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
url: 连接 URL,格式:
|
|
106
|
+
- memory:// 内存(单进程,默认)
|
|
107
|
+
- redis://host:port Redis Pub/Sub
|
|
108
|
+
- kafka://host:port Apache Kafka
|
|
109
|
+
- postgres://... PostgreSQL
|
|
110
|
+
- amqp://... RabbitMQ(需 backend=rabbitmq)
|
|
111
|
+
channel_prefix: 事件频道前缀,默认 "aury:event:"
|
|
111
112
|
exchange_name: RabbitMQ 交换机名称,默认 "aury.events"
|
|
112
113
|
|
|
113
114
|
Returns:
|
|
@@ -132,17 +133,20 @@ class EventBusManager:
|
|
|
132
133
|
|
|
133
134
|
self._backend_type = backend
|
|
134
135
|
|
|
135
|
-
#
|
|
136
|
-
if backend == EventBackend.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
kwargs = {"url": url, "redis_client": redis_client}
|
|
136
|
+
# 根据后端类型创建实例
|
|
137
|
+
if backend == EventBackend.BROADCASTER:
|
|
138
|
+
# 默认使用内存
|
|
139
|
+
effective_url = url or "memory://"
|
|
140
|
+
kwargs: dict[str, Any] = {"url": effective_url}
|
|
141
141
|
if channel_prefix is not None:
|
|
142
142
|
kwargs["channel_prefix"] = channel_prefix
|
|
143
|
-
self._backend =
|
|
143
|
+
self._backend = BroadcasterEventBus(**kwargs)
|
|
144
144
|
elif backend == EventBackend.RABBITMQ:
|
|
145
|
+
if not url:
|
|
146
|
+
raise ValueError("RabbitMQ 后端需要提供 url 参数")
|
|
145
147
|
self._backend = RabbitMQEventBus(url=url, exchange_name=exchange_name)
|
|
148
|
+
elif backend == EventBackend.ROCKETMQ:
|
|
149
|
+
raise NotImplementedError("RocketMQ 后端尚未实现")
|
|
146
150
|
else:
|
|
147
151
|
supported = ", ".join(b.value for b in EventBackend)
|
|
148
152
|
raise ValueError(f"不支持的事件总线后端: {backend}。支持: {supported}")
|