122 lines
3.9 KiB
Python
122 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
通信适配器抽象基类
|
||
所有中间件适配器必须继承此类并实现全部抽象方法
|
||
遵循 Factory + Strategy 模式
|
||
"""
|
||
|
||
import asyncio
|
||
import logging
|
||
from abc import ABC, abstractmethod
|
||
from dataclasses import dataclass
|
||
from typing import Callable, Awaitable, List, Optional
|
||
|
||
from uas.comm.message import Message
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 消息处理器类型:同步 or 异步均可
|
||
MessageHandler = Callable[[Message], Optional[Awaitable[None]]]
|
||
|
||
|
||
@dataclass
|
||
class ConnectionConfig:
|
||
"""通用连接配置(由 URL 解析填充)"""
|
||
scheme: str # redis / kafka / dds
|
||
host: str = "localhost"
|
||
port: int = 0
|
||
username: str = ""
|
||
password: str = ""
|
||
database: str = "" # redis db index / kafka group id / dds domain
|
||
options: dict = None # URL query 参数
|
||
|
||
def __post_init__(self):
|
||
if self.options is None:
|
||
self.options = {}
|
||
|
||
|
||
class CommBaseAdapter(ABC):
|
||
"""
|
||
所有通信适配器的抽象基类
|
||
子类只需实现以下 5 个核心方法
|
||
"""
|
||
|
||
def __init__(self, config: ConnectionConfig):
|
||
self.config = config
|
||
self.logger = logging.getLogger(self.__class__.__name__)
|
||
self._running = False
|
||
self._handlers: dict[str, List[MessageHandler]] = {}
|
||
|
||
# ── 生命周期 ──────────────────────────────────────────────────
|
||
|
||
@abstractmethod
|
||
async def connect(self) -> None:
|
||
"""建立连接"""
|
||
...
|
||
|
||
@abstractmethod
|
||
async def disconnect(self) -> None:
|
||
"""断开连接"""
|
||
...
|
||
|
||
@property
|
||
def is_connected(self) -> bool:
|
||
return self._running
|
||
|
||
# ── 核心功能 ──────────────────────────────────────────────────
|
||
|
||
@abstractmethod
|
||
async def publish(self, message: Message) -> bool:
|
||
"""
|
||
发布消息到指定 topic
|
||
:return: True 表示发送成功
|
||
"""
|
||
...
|
||
|
||
@abstractmethod
|
||
async def subscribe(self, topic: str, handler: MessageHandler) -> None:
|
||
"""
|
||
订阅 topic,注册消息处理回调
|
||
支持通配符(由各适配器自行实现)
|
||
"""
|
||
...
|
||
|
||
@abstractmethod
|
||
async def unsubscribe(self, topic: str) -> None:
|
||
"""取消订阅"""
|
||
...
|
||
|
||
# ── 可选扩展 ──────────────────────────────────────────────────
|
||
|
||
async def request(self, message: Message, timeout: float = 5.0) -> Optional[Message]:
|
||
"""
|
||
Request/Reply 模式(默认未实现,子类可覆盖)
|
||
"""
|
||
raise NotImplementedError(f"{self.__class__.__name__} 不支持 Request/Reply 模式")
|
||
|
||
async def health_check(self) -> bool:
|
||
"""健康检查,默认返回连接状态"""
|
||
return self._running
|
||
|
||
# ── 内部工具 ──────────────────────────────────────────────────
|
||
|
||
def _register_handler(self, topic: str, handler: MessageHandler):
|
||
self._handlers.setdefault(topic, []).append(handler)
|
||
|
||
async def _dispatch(self, message: Message):
|
||
"""将消息分发给所有匹配的 handler"""
|
||
handlers = self._handlers.get(message.topic, [])
|
||
for handler in handlers:
|
||
try:
|
||
result = handler(message)
|
||
if asyncio.iscoroutine(result):
|
||
await result
|
||
except Exception as e:
|
||
self.logger.error(f"Handler 执行异常 topic={message.topic}: {e}", exc_info=True)
|
||
|
||
async def __aenter__(self):
|
||
await self.connect()
|
||
return self
|
||
|
||
async def __aexit__(self, *args):
|
||
await self.disconnect() |