203 lines
7.5 KiB
Python
203 lines
7.5 KiB
Python
"""提供 Platform IO 的路由绑定存储与归属解析能力。"""
|
||
|
||
from typing import Dict, List, Optional
|
||
|
||
from .types import RouteBinding, RouteKey, RouteMode
|
||
|
||
|
||
class RouteBindingConflictError(ValueError):
|
||
"""当同一路由键出现多个 active owner 竞争时抛出。"""
|
||
|
||
|
||
class RouteTable:
|
||
"""维护路由绑定并解析路由归属。
|
||
|
||
这个表刻意保持轻量,只负责归属规则本身,不掺杂具体发送或接收逻辑。
|
||
它决定某个路由键当前由哪个驱动 active 接管,哪些驱动仅以 shadow
|
||
方式旁路观测。
|
||
"""
|
||
|
||
def __init__(self) -> None:
|
||
"""初始化一个空的路由绑定表。"""
|
||
self._bindings: Dict[RouteKey, Dict[str, RouteBinding]] = {}
|
||
|
||
def bind(self, binding: RouteBinding, *, replace: bool = False) -> None:
|
||
"""注册或更新一条路由绑定。
|
||
|
||
Args:
|
||
binding: 要注册的绑定对象。
|
||
replace: 当精确路由键上已经存在 active owner 时,是否允许替换。
|
||
|
||
Raises:
|
||
RouteBindingConflictError: 当精确路由键上已存在其他 active owner,
|
||
且 ``replace`` 为 ``False`` 时抛出。
|
||
"""
|
||
|
||
if binding.mode == RouteMode.DISABLED:
|
||
self.unbind(binding.route_key, binding.driver_id)
|
||
return
|
||
|
||
if binding.mode == RouteMode.ACTIVE:
|
||
active_binding = self.get_active_binding(binding.route_key, exact_only=True)
|
||
if active_binding and active_binding.driver_id != binding.driver_id:
|
||
if not replace:
|
||
raise RouteBindingConflictError(
|
||
f"RouteKey {binding.route_key} 已由 {active_binding.driver_id} 接管,"
|
||
f"拒绝绑定到 {binding.driver_id}"
|
||
)
|
||
self.unbind(binding.route_key, active_binding.driver_id)
|
||
|
||
self._bindings.setdefault(binding.route_key, {})[binding.driver_id] = binding
|
||
|
||
def unbind(self, route_key: RouteKey, driver_id: Optional[str] = None) -> List[RouteBinding]:
|
||
"""移除指定路由键上的绑定。
|
||
|
||
Args:
|
||
route_key: 要移除绑定的路由键。
|
||
driver_id: 可选的特定驱动 ID;若为空,则移除该路由键上的全部绑定。
|
||
|
||
Returns:
|
||
List[RouteBinding]: 被移除的绑定列表。
|
||
"""
|
||
|
||
binding_map = self._bindings.get(route_key)
|
||
if not binding_map:
|
||
return []
|
||
|
||
if driver_id is None:
|
||
removed = list(binding_map.values())
|
||
self._bindings.pop(route_key, None)
|
||
return removed
|
||
|
||
removed_binding = binding_map.pop(driver_id, None)
|
||
if not binding_map:
|
||
self._bindings.pop(route_key, None)
|
||
return [removed_binding] if removed_binding else []
|
||
|
||
def remove_bindings_by_driver(self, driver_id: str) -> List[RouteBinding]:
|
||
"""移除某个驱动在所有路由键上的绑定。
|
||
|
||
Args:
|
||
driver_id: 要移除绑定的驱动 ID。
|
||
|
||
Returns:
|
||
List[RouteBinding]: 被移除的绑定列表。
|
||
"""
|
||
removed_bindings: List[RouteBinding] = []
|
||
empty_route_keys: List[RouteKey] = []
|
||
|
||
for route_key, binding_map in self._bindings.items():
|
||
removed_binding = binding_map.pop(driver_id, None)
|
||
if removed_binding is not None:
|
||
removed_bindings.append(removed_binding)
|
||
if not binding_map:
|
||
empty_route_keys.append(route_key)
|
||
|
||
for route_key in empty_route_keys:
|
||
self._bindings.pop(route_key, None)
|
||
|
||
return self._sort_bindings(removed_bindings)
|
||
|
||
def list_bindings(self, route_key: Optional[RouteKey] = None) -> List[RouteBinding]:
|
||
"""列出当前绑定。
|
||
|
||
Args:
|
||
route_key: 可选的路由键过滤条件;若为空,则返回全部路由键上的绑定。
|
||
|
||
Returns:
|
||
List[RouteBinding]: 按优先级降序排列的绑定列表。
|
||
"""
|
||
|
||
if route_key is None:
|
||
bindings: List[RouteBinding] = []
|
||
for binding_map in self._bindings.values():
|
||
bindings.extend(binding_map.values())
|
||
return self._sort_bindings(bindings)
|
||
|
||
binding_map = self._bindings.get(route_key, {})
|
||
return self._sort_bindings(list(binding_map.values()))
|
||
|
||
def get_active_binding(self, route_key: RouteKey, *, exact_only: bool = False) -> Optional[RouteBinding]:
|
||
"""获取某个路由键当前生效的 active 绑定。
|
||
|
||
Args:
|
||
route_key: 要解析的路由键。
|
||
exact_only: 是否只检查精确路由键而不做回退解析。
|
||
|
||
Returns:
|
||
Optional[RouteBinding]: 若存在 active owner,则返回对应绑定。
|
||
"""
|
||
|
||
candidate_keys = [route_key] if exact_only else route_key.resolution_order()
|
||
for candidate_key in candidate_keys:
|
||
binding_map = self._bindings.get(candidate_key, {})
|
||
active_binding = self._pick_best_binding(binding_map, RouteMode.ACTIVE)
|
||
if active_binding is not None:
|
||
return active_binding
|
||
return None
|
||
|
||
def get_shadow_bindings(self, route_key: RouteKey) -> List[RouteBinding]:
|
||
"""获取某个精确路由键上的 shadow 绑定。
|
||
|
||
Args:
|
||
route_key: 要查看的路由键。
|
||
|
||
Returns:
|
||
List[RouteBinding]: 按优先级降序排列的 shadow 绑定列表。
|
||
"""
|
||
binding_map = self._bindings.get(route_key, {})
|
||
shadow_bindings = [binding for binding in binding_map.values() if binding.mode == RouteMode.SHADOW]
|
||
return self._sort_bindings(shadow_bindings)
|
||
|
||
def accepts_inbound(self, route_key: RouteKey, driver_id: str) -> bool:
|
||
"""判断某个驱动是否是当前允许入 Core 的 active owner。
|
||
|
||
Args:
|
||
route_key: 入站消息对应的路由键。
|
||
driver_id: 希望将消息送入 Core 的驱动 ID。
|
||
|
||
Returns:
|
||
bool: 若该驱动是解析结果中的 active owner,则返回 ``True``。
|
||
"""
|
||
|
||
active_binding = self.get_active_binding(route_key)
|
||
return active_binding is not None and active_binding.driver_id == driver_id
|
||
|
||
@staticmethod
|
||
def _sort_bindings(bindings: List[RouteBinding]) -> List[RouteBinding]:
|
||
"""按优先级降序排列绑定列表。
|
||
|
||
Args:
|
||
bindings: 待排序的绑定列表。
|
||
|
||
Returns:
|
||
List[RouteBinding]: 排序后的绑定列表。
|
||
"""
|
||
return sorted(bindings, key=lambda item: item.priority, reverse=True)
|
||
|
||
@staticmethod
|
||
def _pick_best_binding(
|
||
binding_map: Dict[str, RouteBinding],
|
||
mode: RouteMode,
|
||
) -> Optional[RouteBinding]:
|
||
"""从绑定映射中挑选指定模式下优先级最高的一条绑定。
|
||
|
||
Args:
|
||
binding_map: 某个精确 ``RouteKey`` 对应的绑定映射。
|
||
mode: 需要挑选的绑定模式。
|
||
|
||
Returns:
|
||
Optional[RouteBinding]: 若存在匹配模式的绑定,则返回优先级最高的一条。
|
||
|
||
Notes:
|
||
这里使用单次线性扫描代替“先过滤成列表再排序”的做法,以减少
|
||
高频路由解析路径上的临时对象分配和排序开销。
|
||
"""
|
||
best_binding: Optional[RouteBinding] = None
|
||
for binding in binding_map.values():
|
||
if binding.mode != mode:
|
||
continue
|
||
if best_binding is None or binding.priority > best_binding.priority:
|
||
best_binding = binding
|
||
return best_binding
|