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 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.23'
32
- __version_tuple__ = version_tuple = (0, 0, 23)
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",
@@ -41,6 +41,9 @@ class ComponentName(str, Enum):
41
41
  # 事件总线组件
42
42
  EVENT_BUS = "event_bus"
43
43
 
44
+ # 流式通道组件
45
+ CHANNEL = "channel"
46
+
44
47
  # 迁移组件
45
48
  MIGRATIONS = "migrations"
46
49
 
@@ -20,10 +20,7 @@ notification_channel = ChannelManager.get_instance("notification")
20
20
  await sse_channel.initialize(backend="memory")
21
21
 
22
22
  # Redis 后端(多进程/分布式)
23
- from aury.boot.infrastructure.clients.redis import RedisClient
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", redis_client=redis_client) # 需要跨进程
129
- await notify_channel.initialize(backend="redis", redis_client=redis_client)
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
- redis_client: RedisClient | None = None,
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
- redis_client: Redis 客户端(当 backend=redis 时需要)
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 redis_client is None:
118
- raise ValueError("Redis 通道需要提供 redis_client 参数")
119
- self._backend = RedisChannel(redis_client)
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
- self._initialized = False
218
- logger.info(f"通道管理器 [{self.name}] 已关闭")
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aury-boot
3
- Version: 0.0.23
3
+ Version: 0.0.25
4
4
  Summary: Aury Boot - 基于 FastAPI 生态的企业级 API 开发框架
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: alembic>=1.17.2
@@ -1,5 +1,5 @@
1
1
  aury/boot/__init__.py,sha256=pCno-EInnpIBa1OtxNYF-JWf9j95Cd2h6vmu0xqa_-4,1791
2
- aury/boot/_version.py,sha256=ibKwfdtUKJ8ftEJ-ANHeQVfItYjoZhYBzigrJx5AdJU,706
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=-blDjMF87GYFmFyk4DHm2KJDcm27HpMxGpNaEFluwEE,22403
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=hDRs3YxpnfIFcGaUa1DYqBRwmV2_dceOlcCXabHE3fk,1006
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=xdWsIO4vT-0V1BKvPgWiG7DaQwKQSFdv7CeS16iS0cQ,4578
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=kHdeVzo7btivJs8TyGDu2QgucnAlLqr-hL1dJegD72U,7166
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.23.dist-info/METADATA,sha256=019uIaM7hlX1CuRtQZpXpEFzDNkBowKBsad1PRyXcBc,7695
196
- aury_boot-0.0.23.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
197
- aury_boot-0.0.23.dist-info/entry_points.txt,sha256=f9KXEkDIGc0BGkgBvsNx_HMz9VhDjNxu26q00jUpDwQ,49
198
- aury_boot-0.0.23.dist-info/RECORD,,
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,,