125 lines
4.6 KiB
Python
125 lines
4.6 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
通信中间件工厂
|
|||
|
|
─────────────────────────────────────────────────────────────────
|
|||
|
|
支持通过 URL 自动路由到对应适配器:
|
|||
|
|
|
|||
|
|
redis://[:password@]host[:port][/db][?options]
|
|||
|
|
kafka://[user:password@]host[:port][/group_id][?options]
|
|||
|
|
dds://[domain_id@]host[:port][/topic_prefix][?options]
|
|||
|
|
|
|||
|
|
扩展新协议只需:
|
|||
|
|
1. 实现 CommBaseAdapter 子类
|
|||
|
|
2. 调用 CommAdapterFactory.register("scheme", YourAdapter)
|
|||
|
|
─────────────────────────────────────────────────────────────────
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
from typing import Type, Dict
|
|||
|
|
from urllib.parse import urlparse, parse_qs, unquote
|
|||
|
|
|
|||
|
|
from uas.comm.base import CommBaseAdapter, ConnectionConfig
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CommAdapterFactory:
|
|||
|
|
"""
|
|||
|
|
适配器工厂(注册表模式)
|
|||
|
|
线程安全,支持运行时动态注册新协议
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
_registry: Dict[str, Type[CommBaseAdapter]] = {}
|
|||
|
|
|
|||
|
|
# ── 注册 ──────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def register(cls, scheme: str, adapter_cls: Type[CommBaseAdapter]) -> None:
|
|||
|
|
"""
|
|||
|
|
注册新的协议适配器
|
|||
|
|
:param scheme: 协议名,如 "redis"、"kafka"、"mqtt"
|
|||
|
|
:param adapter_cls: CommBaseAdapter 子类
|
|||
|
|
"""
|
|||
|
|
scheme = scheme.lower()
|
|||
|
|
if not issubclass(adapter_cls, CommBaseAdapter):
|
|||
|
|
raise TypeError(f"{adapter_cls} 必须继承自 CommBaseAdapter")
|
|||
|
|
cls._registry[scheme] = adapter_cls
|
|||
|
|
logger.info(f"✅ 已注册适配器: {scheme} -> {adapter_cls.__name__}")
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def unregister(cls, scheme: str) -> None:
|
|||
|
|
cls._registry.pop(scheme.lower(), None)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def available_schemes(cls) -> list:
|
|||
|
|
return list(cls._registry.keys())
|
|||
|
|
|
|||
|
|
# ── 创建 ──────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def create(cls, url: str) -> CommBaseAdapter:
|
|||
|
|
"""
|
|||
|
|
根据 URL 自动创建对应适配器实例
|
|||
|
|
示例:
|
|||
|
|
redis://:mypassword@127.0.0.1:6379/0?socket_timeout=5
|
|||
|
|
kafka://broker1:9092/my-group?auto_offset_reset=earliest
|
|||
|
|
dds://0@localhost:7400/rt?reliability=RELIABLE
|
|||
|
|
"""
|
|||
|
|
config = cls._parse_url(url)
|
|||
|
|
scheme = config.scheme
|
|||
|
|
|
|||
|
|
if scheme not in cls._registry:
|
|||
|
|
raise ValueError(
|
|||
|
|
f"未知协议: '{scheme}',已注册: {cls.available_schemes()}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
adapter_cls = cls._registry[scheme]
|
|||
|
|
logger.info(f"🔧 创建适配器: {adapter_cls.__name__} url={url!r}")
|
|||
|
|
return adapter_cls(config)
|
|||
|
|
|
|||
|
|
# ── URL 解析 ──────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def _parse_url(cls, url: str) -> ConnectionConfig:
|
|||
|
|
"""
|
|||
|
|
解析通用 URL 为 ConnectionConfig
|
|||
|
|
兼容以下格式:
|
|||
|
|
redis://[:password@]host[:port][/db]
|
|||
|
|
kafka://[user:pass@]host[:port][/group]
|
|||
|
|
dds://[domain@]host[:port][/prefix]
|
|||
|
|
"""
|
|||
|
|
parsed = urlparse(url)
|
|||
|
|
scheme = parsed.scheme.lower()
|
|||
|
|
|
|||
|
|
# 默认端口映射
|
|||
|
|
default_ports = {
|
|||
|
|
"redis": 6379,
|
|||
|
|
"kafka": 9092,
|
|||
|
|
"dds": 7400,
|
|||
|
|
"mqtt": 1883,
|
|||
|
|
"nats": 4222,
|
|||
|
|
"amqp": 5672,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
host = parsed.hostname or "localhost"
|
|||
|
|
port = parsed.port or default_ports.get(scheme, 0)
|
|||
|
|
username = unquote(parsed.username or "")
|
|||
|
|
password = unquote(parsed.password or "")
|
|||
|
|
database = parsed.path.lstrip("/") if parsed.path else ""
|
|||
|
|
|
|||
|
|
# 解析 query 参数(所有值展开为单值)
|
|||
|
|
raw_opts = parse_qs(parsed.query, keep_blank_values=True)
|
|||
|
|
options = {k: v[0] if len(v) == 1 else v for k, v in raw_opts.items()}
|
|||
|
|
|
|||
|
|
config = ConnectionConfig(
|
|||
|
|
scheme = scheme,
|
|||
|
|
host = host,
|
|||
|
|
port = port,
|
|||
|
|
username = username,
|
|||
|
|
password = password,
|
|||
|
|
database = database,
|
|||
|
|
options = options,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
logger.debug(f"URL 解析结果: {config}")
|
|||
|
|
return config
|