aury-boot 0.0.23__py3-none-any.whl → 0.0.25__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/app/components.py +46 -0
- aury/boot/application/constants/components.py +3 -0
- aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl +4 -7
- aury/boot/infrastructure/channel/manager.py +18 -11
- {aury_boot-0.0.23.dist-info → aury_boot-0.0.25.dist-info}/METADATA +1 -1
- {aury_boot-0.0.23.dist-info → aury_boot-0.0.25.dist-info}/RECORD +9 -9
- {aury_boot-0.0.23.dist-info → aury_boot-0.0.25.dist-info}/WHEEL +0 -0
- {aury_boot-0.0.23.dist-info → aury_boot-0.0.25.dist-info}/entry_points.txt +0 -0
aury/boot/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.25'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 25)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -15,6 +15,7 @@ from aury.boot.application.constants import ComponentName, ServiceType
|
|
|
15
15
|
from aury.boot.application.migrations import MigrationManager
|
|
16
16
|
from aury.boot.common.logging import logger
|
|
17
17
|
from aury.boot.infrastructure.cache import CacheManager
|
|
18
|
+
from aury.boot.infrastructure.channel import ChannelManager
|
|
18
19
|
from aury.boot.infrastructure.database import DatabaseManager
|
|
19
20
|
from aury.boot.infrastructure.events import EventBusManager
|
|
20
21
|
from aury.boot.infrastructure.mq import MQManager
|
|
@@ -505,6 +506,49 @@ class MessageQueueComponent(Component):
|
|
|
505
506
|
logger.warning(f"消息队列 [{name}] 关闭失败: {e}")
|
|
506
507
|
|
|
507
508
|
|
|
509
|
+
class ChannelComponent(Component):
|
|
510
|
+
"""流式通道组件。
|
|
511
|
+
|
|
512
|
+
用于 SSE(Server-Sent Events)和实时通信场景,支持 memory 和 redis 后端。
|
|
513
|
+
"""
|
|
514
|
+
|
|
515
|
+
name = ComponentName.CHANNEL
|
|
516
|
+
enabled = True
|
|
517
|
+
depends_on: ClassVar[list[str]] = []
|
|
518
|
+
|
|
519
|
+
def can_enable(self, config: BaseConfig) -> bool:
|
|
520
|
+
"""当配置了 Channel 实例时启用。"""
|
|
521
|
+
return self.enabled and bool(config.get_channels())
|
|
522
|
+
|
|
523
|
+
async def setup(self, app: FoundationApp, config: BaseConfig) -> None:
|
|
524
|
+
"""初始化流式通道。"""
|
|
525
|
+
channel_configs = config.get_channels()
|
|
526
|
+
if not channel_configs:
|
|
527
|
+
logger.debug("未配置 Channel 实例,跳过通道初始化")
|
|
528
|
+
return
|
|
529
|
+
|
|
530
|
+
for name, ch_config in channel_configs.items():
|
|
531
|
+
try:
|
|
532
|
+
channel_manager = ChannelManager.get_instance(name)
|
|
533
|
+
if not channel_manager.is_initialized:
|
|
534
|
+
await channel_manager.initialize(
|
|
535
|
+
backend=ch_config.backend,
|
|
536
|
+
url=ch_config.url,
|
|
537
|
+
)
|
|
538
|
+
except Exception as e:
|
|
539
|
+
logger.warning(f"通道 [{name}] 初始化失败(非关键): {e}")
|
|
540
|
+
|
|
541
|
+
async def teardown(self, app: FoundationApp) -> None:
|
|
542
|
+
"""关闭所有通道实例。"""
|
|
543
|
+
for name in list(ChannelManager._instances.keys()):
|
|
544
|
+
try:
|
|
545
|
+
channel_manager = ChannelManager.get_instance(name)
|
|
546
|
+
if channel_manager.is_initialized:
|
|
547
|
+
await channel_manager.cleanup()
|
|
548
|
+
except Exception as e:
|
|
549
|
+
logger.warning(f"通道 [{name}] 关闭失败: {e}")
|
|
550
|
+
|
|
551
|
+
|
|
508
552
|
class EventBusComponent(Component):
|
|
509
553
|
"""事件总线组件。
|
|
510
554
|
|
|
@@ -555,6 +599,7 @@ FoundationApp.components = [
|
|
|
555
599
|
StorageComponent,
|
|
556
600
|
TaskComponent,
|
|
557
601
|
MessageQueueComponent,
|
|
602
|
+
ChannelComponent,
|
|
558
603
|
EventBusComponent,
|
|
559
604
|
SchedulerComponent,
|
|
560
605
|
]
|
|
@@ -563,6 +608,7 @@ FoundationApp.components = [
|
|
|
563
608
|
__all__ = [
|
|
564
609
|
"AdminConsoleComponent",
|
|
565
610
|
"CacheComponent",
|
|
611
|
+
"ChannelComponent",
|
|
566
612
|
"DatabaseComponent",
|
|
567
613
|
"EventBusComponent",
|
|
568
614
|
"MessageQueueComponent",
|
|
@@ -20,10 +20,7 @@ notification_channel = ChannelManager.get_instance("notification")
|
|
|
20
20
|
await sse_channel.initialize(backend="memory")
|
|
21
21
|
|
|
22
22
|
# Redis 后端(多进程/分布式)
|
|
23
|
-
|
|
24
|
-
redis_client = RedisClient.get_instance()
|
|
25
|
-
await redis_client.initialize(url="redis://localhost:6379/0")
|
|
26
|
-
await notification_channel.initialize(backend="redis", redis_client=redis_client)
|
|
23
|
+
await notification_channel.initialize(backend="redis", url="redis://localhost:6379/0")
|
|
27
24
|
```
|
|
28
25
|
|
|
29
26
|
## 13.2 发布和订阅(Topic 管理)
|
|
@@ -85,7 +82,7 @@ async def send_notification(user_id: str, message: str):
|
|
|
85
82
|
async def space_events(space_id: str):
|
|
86
83
|
\"\"\"订阅空间下所有事件。\"\"\"
|
|
87
84
|
async def event_generator():
|
|
88
|
-
# 使用 psubscribe 订阅 space:{id}:* 下所有事件
|
|
85
|
+
# 使用 psubscribe 订阅 space:{{id}}:* 下所有事件
|
|
89
86
|
async for msg in sse_channel.psubscribe(f"space:{{space_id}}:*"):
|
|
90
87
|
yield msg.to_sse() # 自动转换为 SSE 格式
|
|
91
88
|
|
|
@@ -125,8 +122,8 @@ notify_channel = ChannelManager.get_instance("notify") # 系统通知
|
|
|
125
122
|
|
|
126
123
|
# 分别初始化(可使用不同后端)
|
|
127
124
|
await sse_channel.initialize(backend="memory") # 单进程即可
|
|
128
|
-
await chat_channel.initialize(backend="redis",
|
|
129
|
-
await notify_channel.initialize(backend="redis",
|
|
125
|
+
await chat_channel.initialize(backend="redis", url="redis://localhost:6379/3") # 需要跨进程
|
|
126
|
+
await notify_channel.initialize(backend="redis", url="redis://localhost:6379/4")
|
|
130
127
|
```
|
|
131
128
|
|
|
132
129
|
## 13.6 环境变量
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from collections.abc import AsyncIterator
|
|
9
|
-
from typing import TYPE_CHECKING
|
|
10
9
|
|
|
11
10
|
from aury.boot.common.logging import logger
|
|
12
11
|
|
|
@@ -14,9 +13,6 @@ from .backends.memory import MemoryChannel
|
|
|
14
13
|
from .backends.redis import RedisChannel
|
|
15
14
|
from .base import ChannelBackend, ChannelMessage, IChannel
|
|
16
15
|
|
|
17
|
-
if TYPE_CHECKING:
|
|
18
|
-
from aury.boot.infrastructure.clients.redis import RedisClient
|
|
19
|
-
|
|
20
16
|
|
|
21
17
|
class ChannelManager:
|
|
22
18
|
"""通道管理器(命名多实例)。
|
|
@@ -55,6 +51,8 @@ class ChannelManager:
|
|
|
55
51
|
self._backend: IChannel | None = None
|
|
56
52
|
self._backend_type: ChannelBackend | None = None
|
|
57
53
|
self._initialized: bool = False
|
|
54
|
+
self._redis_client = None
|
|
55
|
+
self._url: str | None = None
|
|
58
56
|
|
|
59
57
|
@classmethod
|
|
60
58
|
def get_instance(cls, name: str = "default") -> ChannelManager:
|
|
@@ -88,14 +86,14 @@ class ChannelManager:
|
|
|
88
86
|
self,
|
|
89
87
|
backend: ChannelBackend | str = ChannelBackend.MEMORY,
|
|
90
88
|
*,
|
|
91
|
-
|
|
89
|
+
url: str | None = None,
|
|
92
90
|
max_subscribers: int = 1000,
|
|
93
91
|
) -> ChannelManager:
|
|
94
92
|
"""初始化通道(链式调用)。
|
|
95
93
|
|
|
96
94
|
Args:
|
|
97
95
|
backend: 后端类型
|
|
98
|
-
|
|
96
|
+
url: Redis 连接 URL(当 backend=redis 时需要)
|
|
99
97
|
max_subscribers: 内存后端的最大订阅者数量
|
|
100
98
|
|
|
101
99
|
Returns:
|
|
@@ -110,13 +108,19 @@ class ChannelManager:
|
|
|
110
108
|
backend = ChannelBackend(backend.lower())
|
|
111
109
|
|
|
112
110
|
self._backend_type = backend
|
|
111
|
+
self._url = url
|
|
113
112
|
|
|
114
113
|
if backend == ChannelBackend.MEMORY:
|
|
115
114
|
self._backend = MemoryChannel(max_subscribers=max_subscribers)
|
|
116
115
|
elif backend == ChannelBackend.REDIS:
|
|
117
|
-
if
|
|
118
|
-
raise ValueError("Redis 通道需要提供
|
|
119
|
-
|
|
116
|
+
if url is None:
|
|
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)
|
|
120
124
|
else:
|
|
121
125
|
raise ValueError(f"不支持的通道后端: {backend}")
|
|
122
126
|
|
|
@@ -214,8 +218,11 @@ class ChannelManager:
|
|
|
214
218
|
if self._backend:
|
|
215
219
|
await self._backend.close()
|
|
216
220
|
self._backend = None
|
|
217
|
-
|
|
218
|
-
|
|
221
|
+
if self._redis_client:
|
|
222
|
+
await self._redis_client.cleanup()
|
|
223
|
+
self._redis_client = None
|
|
224
|
+
self._initialized = False
|
|
225
|
+
logger.info(f"通道管理器 [{self.name}] 已关闭")
|
|
219
226
|
|
|
220
227
|
def __repr__(self) -> str:
|
|
221
228
|
"""字符串表示。"""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
aury/boot/__init__.py,sha256=pCno-EInnpIBa1OtxNYF-JWf9j95Cd2h6vmu0xqa_-4,1791
|
|
2
|
-
aury/boot/_version.py,sha256=
|
|
2
|
+
aury/boot/_version.py,sha256=DspPu1FRRiGe5F0Nbz1wXOpsS5e-m6ETOzBjU5BFWRE,706
|
|
3
3
|
aury/boot/application/__init__.py,sha256=0o_XmiwFCeAu06VHggS8I1e7_nSMoRq0Hcm0fYfCywU,3071
|
|
4
4
|
aury/boot/application/adapter/__init__.py,sha256=e1bcSb1bxUMfofTwiCuHBZJk5-STkMCWPF2EJXHQ7UU,3976
|
|
5
5
|
aury/boot/application/adapter/base.py,sha256=Ar_66fiHPDEmV-1DKnqXKwc53p3pozG31bgTJTEUriY,15763
|
|
@@ -9,14 +9,14 @@ aury/boot/application/adapter/exceptions.py,sha256=Kzm-ytRxdUnSMIcWCSOHPxo4Jh_A6
|
|
|
9
9
|
aury/boot/application/adapter/http.py,sha256=4TADsSzdSRU63307dmmo-2U_JpVP12mwTFy66B5Ps-w,10759
|
|
10
10
|
aury/boot/application/app/__init__.py,sha256=I8FfCKDuDQsGzAK6BevyfdtAwieMUVYu6qgVQzBazpE,830
|
|
11
11
|
aury/boot/application/app/base.py,sha256=MYfkrb2zZgumLrGWhtT71neGncUfVHyUeAHRnetoSXk,17668
|
|
12
|
-
aury/boot/application/app/components.py,sha256
|
|
12
|
+
aury/boot/application/app/components.py,sha256=qBAd8hJ4Ap3cfCYJUJu9v_IvC16U9Q8O5dEZDtjDveY,24188
|
|
13
13
|
aury/boot/application/app/middlewares.py,sha256=BXe2H14FHzJUVpQM6DZUm-zfZRXSXIi1QIZ4_3izfHw,3306
|
|
14
14
|
aury/boot/application/app/startup.py,sha256=DHKt3C2G7V5XfFr1SQMl14tNzcuDd9MqUVAxi274HDQ,7873
|
|
15
15
|
aury/boot/application/config/__init__.py,sha256=Dd-myRSBCM18DXXsi863h0cJG5VFrI10xMRtjnvelGo,1894
|
|
16
16
|
aury/boot/application/config/multi_instance.py,sha256=RXSp-xP8-bKMDEhq3SeL7T3lS8-vpRlvBEVBuZVjVK4,6475
|
|
17
17
|
aury/boot/application/config/settings.py,sha256=o0SAwEpFqoHE7zB3lYyaSCm0cX2NZnTVqUWiSJ-Qdtk,30576
|
|
18
18
|
aury/boot/application/constants/__init__.py,sha256=DCXs13_VVaQWHqO-qpJoZwRd7HIexiirtw_nu8msTXE,340
|
|
19
|
-
aury/boot/application/constants/components.py,sha256=
|
|
19
|
+
aury/boot/application/constants/components.py,sha256=VBCgxJ_iZXjEZyUkC13ZCdqPmdKq-lQmU7jQpXnTa2Y,1056
|
|
20
20
|
aury/boot/application/constants/scheduler.py,sha256=S77FBIvHlyruvlabRWZJ2J1YAs2xWXPQI2yuGdGUDNA,471
|
|
21
21
|
aury/boot/application/constants/service.py,sha256=_BjSNP83m1KuLcGs1oqciDU9Nk1mO45COucRYubuFkM,513
|
|
22
22
|
aury/boot/application/errors/__init__.py,sha256=aYhGqjHayYr7sv9kM22y0sOo9R-8RK0r3Jf5_tgm7TQ,1217
|
|
@@ -81,7 +81,7 @@ aury/boot/commands/templates/project/aury_docs/09-tasks.md.tpl,sha256=swHOQ_pWPt
|
|
|
81
81
|
aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl,sha256=mhe0j0S51ndPJLjaQ6yD8OPYBEO02NHumJVbBvz2qkw,4320
|
|
82
82
|
aury/boot/commands/templates/project/aury_docs/11-logging.md.tpl,sha256=bwxFCGQsO9cTEbwqJF1xcjsZKP82HRWhIMRUS0c9_ZI,2435
|
|
83
83
|
aury/boot/commands/templates/project/aury_docs/12-admin.md.tpl,sha256=6z3mN54qP2jtpTFOJBLVexvEv0ZHXYKjncvpZG4yOdw,1883
|
|
84
|
-
aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl,sha256=
|
|
84
|
+
aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl,sha256=SV0XpuzzF7YGwsGm9uswQf0-JQvbJ98rdXOUa2KJm5E,4428
|
|
85
85
|
aury/boot/commands/templates/project/aury_docs/14-mq.md.tpl,sha256=4bxLQBbCi0Fue0VQWOPt6acZ5P00BoLkCoLPQe_8k4U,2396
|
|
86
86
|
aury/boot/commands/templates/project/aury_docs/15-events.md.tpl,sha256=a4wQRgVPuYUGTGmw_lX1HJH_yFTbD30mBz7Arc4zgfs,3361
|
|
87
87
|
aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl,sha256=pkmJkZw2Ca6_uYk2jZvAb8DozjBa2tWq_t3gtq1lFSk,11456
|
|
@@ -140,7 +140,7 @@ aury/boot/infrastructure/cache/factory.py,sha256=aF74JoiiSKFgctqqh2Z8OtGRS2Am_ou
|
|
|
140
140
|
aury/boot/infrastructure/cache/manager.py,sha256=GGoOgYyIdWKMmhej5cRvEfpNeMN1GaSaU9hc0dy8_sA,12106
|
|
141
141
|
aury/boot/infrastructure/channel/__init__.py,sha256=Ztcfn1-TomgV91qhePpFK-3_nKgBt862yEFYUzIwPlo,566
|
|
142
142
|
aury/boot/infrastructure/channel/base.py,sha256=kRjvoOQ0X5ilUAdz03BNBfbZJgdqcsjUviK4_dJatBs,2642
|
|
143
|
-
aury/boot/infrastructure/channel/manager.py,sha256=
|
|
143
|
+
aury/boot/infrastructure/channel/manager.py,sha256=wOkO018Ch2694z9vOGgojaGOdCXHi0zrekkfLezn8bo,7444
|
|
144
144
|
aury/boot/infrastructure/channel/backends/__init__.py,sha256=zrOhrzkhEIgsO7Armhgda1ruJQ6a9ZK7GPZuzvEiuN8,151
|
|
145
145
|
aury/boot/infrastructure/channel/backends/memory.py,sha256=QQyZxIdYtA_pWqdKgUIMkvnbBwvv45-vX-qostDHyfg,4699
|
|
146
146
|
aury/boot/infrastructure/channel/backends/redis.py,sha256=_UL7wE-bO147CPXKDjJgYGjj09Lg9x9U2PLYa37q5yQ,4666
|
|
@@ -192,7 +192,7 @@ aury/boot/testing/client.py,sha256=KOg1EemuIVsBG68G5y0DjSxZGcIQVdWQ4ASaHE3o1R0,4
|
|
|
192
192
|
aury/boot/testing/factory.py,sha256=8GvwX9qIDu0L65gzJMlrWB0xbmJ-7zPHuwk3eECULcg,5185
|
|
193
193
|
aury/boot/toolkit/__init__.py,sha256=AcyVb9fDf3CaEmJPNkWC4iGv32qCPyk4BuFKSuNiJRQ,334
|
|
194
194
|
aury/boot/toolkit/http/__init__.py,sha256=zIPmpIZ9Qbqe25VmEr7jixoY2fkRbLm7NkCB9vKpg6I,11039
|
|
195
|
-
aury_boot-0.0.
|
|
196
|
-
aury_boot-0.0.
|
|
197
|
-
aury_boot-0.0.
|
|
198
|
-
aury_boot-0.0.
|
|
195
|
+
aury_boot-0.0.25.dist-info/METADATA,sha256=ph7zu8UJgX6_LBmEERNW8O6C7nc0afNhYEGz9Qm3rQs,7695
|
|
196
|
+
aury_boot-0.0.25.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
197
|
+
aury_boot-0.0.25.dist-info/entry_points.txt,sha256=f9KXEkDIGc0BGkgBvsNx_HMz9VhDjNxu26q00jUpDwQ,49
|
|
198
|
+
aury_boot-0.0.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|