在现代分布式系统中,远程过程调用(RPC)是实现服务间通信的核心技术之一。Python生态提供了多种RPC实现方案,从简单的JSON-RPC到高性能的gRPC,开发者还可以根据需求自定义RPC框架。本文将深入探讨这三种实现方式,帮助你理解RPC的核心原理并选择适合的方案。
一、JSON-RPC:轻量级的RPC方案
1.1 基本概念
JSON-RPC是一种基于JSON的轻量级远程过程调用协议。它使用HTTP作为传输协议,JSON作为数据格式,具有简单易用的特点。
1.2 实现示例
# 服务端
from jsonrpcserver import method, Success, serve
@method
def add(x: int, y: int) -> Success:
return Success(x + y)
@method
def greet(name: str) -> Success:
return Success(f"Hello, {name}!")
if __name__ == "__main__":
serve("localhost", 5000)
# 客户端
import requests
def test_add():
payload = {
"jsonrpc": "2.0",
"method": "add",
"params": {"x": 5, "y": 3},
"id": 1
}
response = requests.post(
"http://localhost:5000",
json=payload,
headers={"Content-Type": "application/json"}
).json()
print("服务器返回:", response)
def test_greet():
payload = {
"jsonrpc": "2.0",
"method": "greet",
"params": {"name": "World"},
"id": 2
}
response = requests.post(
"http://localhost:5000",
json=payload
).json()
print("服务器返回:", response)
if __name__ == "__main__":
test_add()
test_greet()
# 服务器返回: {'jsonrpc': '2.0', 'result': 8, 'id': 1}
# 服务器返回: {'jsonrpc': '2.0', 'result': 'Hello, World!', 'id': 2}
1.3 优缺点分析
优点:
- 简单易用,上手门槛低
- 跨语言支持良好
- 人类可读的JSON格式便于调试
缺点:
- 性能较低,不适合高并发场景
- 缺乏强类型检查
- 功能相对简单,缺少高级特性
二、gRPC:高性能的工业级解决方案
2.1 核心特性
gRPC是Google开发的高性能RPC框架,主要特点包括:
- 基于HTTP/2协议
- 使用Protocol Buffers作为接口定义语言(IDL)
- 支持双向流式通信
- 提供多种语言的实现
2.2 实现示例
# proto文件
syntax = "proto3";
package calculator;
service Calculator {
rpc Add(AddRequest) returns (AddResponse) {}
rpc Greet (GreetRequest) returns (GreetResponse) {}
}
message AddRequest {
int32 x = 1;
int32 y = 2;
}
message AddResponse {
int32 result = 1;
}
message GreetRequest {
string name = 1;
}
message GreetResponse {
string greeting = 1;
}
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. calculator.proto
# 服务端
import grpc
from concurrent import futures
import calculator_pb2
import calculator_pb2_grpc
class CalculatorServicer(calculator_pb2_grpc.CalculatorServicer):
def Add(self, request, context):
result = request.x + request.y
return calculator_pb2.AddResponse(result=result)
def Greet(self, request, context):
return calculator_pb2.GreetResponse(
greeting="Hello, {}".format(request.name)
)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
calculator_pb2_grpc.add_CalculatorServicer_to_server(
CalculatorServicer(), server
)
server.add_insecure_port('[::]:50051')
server.start()
print('Server running on port 50051...')
server.wait_for_termination()
if __name__ == '__main__':
serve()
# 客户端
import grpc
import calculator_pb2
import calculator_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = calculator_pb2_grpc.CalculatorStub(channel)
# 普通调用
add_response = stub.Add(calculator_pb2.AddRequest(x=1, y=2))
print("1 + 2 = %d" % add_response.result)
# 带元数据的调用
metadata = [('client-id', 'python-client')]
greet_response = stub.Greet(
calculator_pb2.GreetRequest(name='Alice'),
metadata=metadata
)
print(greet_response.greeting)
if __name__ == '__main__':
run()
2.4 优缺点分析
优点:
- 高性能,基于HTTP/2和二进制协议
- 强类型接口定义
- 支持双向流式通信
- 丰富的生态和工具链
缺点:
- 学习曲线较陡峭
- 调试相对复杂
- 对动态语言不够友好
三、自定义RPC框架:深入理解RPC本质
3.1 设计思路
我们将实现一个包含以下特性的自定义RPC框架:
- 基于TCP协议的二进制通信
- 自定义消息格式
- 同步/异步支持
- 简单的服务注册与发现
3.2 核心实现
消息协议设计:
+--------+--------+--------+--------+---------------+
| 魔数(2) | 版本(1)| 类型(1)| 长度(4)| 数据(变长) |
+--------+--------+--------+--------+---------------+
3.3 实现示例
# 服务端
import struct
import socket
import threading
import json
from enum import IntEnum
class MessageType(IntEnum):
REQUEST = 1
RESPONSE = 2
ERROR = 3
class RPCServer:
MAGIC_NUMBER = 0xFAFA
VERSION = 1
def __init__(self, host='localhost', port=8000):
self.host = host
self.port = port
self.services = {}
self.running = False
def register_service(self, instance):
service_name = instance.__class__.__name__
self.services[service_name] = instance
def _process_request(self, data):
try:
request = json.loads(data.decode('utf-8'))
service_name = request['service']
method_name = request['method']
args = request.get('args', [])
kwargs = request.get('kwargs', {})
service = self.services.get(service_name)
if not service:
raise ValueError(f'Service {service_name} not found')
method = getattr(service, method_name)
result = method(*args, **kwargs)
return (MessageType.RESPONSE,
json.dumps({'result': result}).encode('utf-8'))
except Exception as e:
return (MessageType.ERROR,
json.dumps({'error': str(e)}).encode('utf-8'))
def _handle_connection(self, conn):
try:
while self.running:
# 读取消息头
header = conn.recv(8)
if len(header) < 8:
break
magic, version, msg_type, length = struct.unpack('>HBBL', header)
if magic != self.MAGIC_NUMBER or version != self.VERSION:
break
# 读取消息体
data = conn.recv(length)
if len(data) < length:
break
response_type, response_data = self._process_request(data)
header = struct.pack('>HBBL', self.MAGIC_NUMBER, self.VERSION, response_type, len(response_data))
conn.sendall(header + response_data)
finally:
conn.close()
def start(self):
self.running = True
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((self.host, self.port))
self.socket.listen(5)
print(f'Server started on {self.host}:{self.port}')
try:
while self.running:
conn, addr = self.socket.accept()
thread = threading.Thread(
target=self._handle_connection,
args=(conn,)
)
thread.start()
except KeyboardInterrupt:
self.stop()
def stop(self):
self.running = False
self.socket.close()
class CalculatorService:
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
class GreeterService:
def greet(self, name):
return f'Hello, {name}!'
if __name__ == '__main__':
server = RPCServer(port=8000)
server.register_service(CalculatorService())
server.register_service(GreeterService())
server.start()
# 客户端
import struct
import socket
import json
from enum import IntEnum
class MessageType(IntEnum):
REQUEST = 1
RESPONSE = 2
ERROR = 3
class RPCClient:
MAGIC_NUMBER = 0xFAFA
VERSION = 1
TIMEOUT = 5
def __init__(self, host='localhost', port=8000):
self.host = host
self.port = port
def __getattr__(self, name):
return RPCServiceProxy(self, name)
def _call(self, service, method, *args, **kwargs):
request = {
'service': service,
'method': method,
'args': args,
'kwargs': kwargs
}
request_data = json.dumps(request).encode('utf-8')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(self.TIMEOUT)
sock.connect((self.host, self.port))
# 发送请求
header = struct.pack('>HBBL', self.MAGIC_NUMBER,
self.VERSION, MessageType.REQUEST,
len(request_data))
sock.sendall(header + request_data)
# 接收响应头
header = sock.recv(8)
if len(header) < 8:
raise ConnectionError('Incomplete header received')
magic, version, msg_type, length = struct.unpack('>HBBL', header)
if magic != self.MAGIC_NUMBER or version != self.VERSION:
raise ConnectionError('Invalid protocol header')
# 接收响应体
data = bytearray()
while len(data) < length:
packet = sock.recv(length - len(data))
if not packet:
raise ConnectionError('Incomplete data received')
data.extend(packet)
response = json.loads(data.decode('utf-8'))
if msg_type == MessageType.ERROR:
raise RuntimeError(response.get('error', 'Unknown error'))
return response.get('result')
class RPCServiceProxy:
def __init__(self, client, service_name):
self.client = client
self.service_name = service_name
def __getattr__(self, method_name):
return lambda *args, **kwargs: self.client._call(
self.service_name, method_name, *args, **kwargs
)
if __name__ == '__main__':
client = RPCClient(port=8000)
result = client.CalculatorService.add(5, 3)
print(f'5 + 3 = {result}')
greeting = client.GreeterService.greet('Alice')
print(greeting)
四、进阶
- 基于发布/订阅模式的服务注册与发现
- 引入连接池,实现异步IO
- 功能增强实现服务发现与负载均衡,熔断与降级机制
4.1 基础协议与常量定义
import time
from enum import IntEnum
from typing import Dict, Any
from dataclasses import dataclass, field
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('custom_rpc')
# 协议常量
MAGIC_NUMBER = 0xFAFA
VERSION = 1
HEADER_LENGTH = 8
class MessageType(IntEnum):
REQUEST = 1
RESPONSE = 2
ERROR = 3
HEARTBEAT = 4
class LoadBalanceStrategy(IntEnum):
ROUND_ROBIN = 1
RANDOM = 2
LEAST_CONN = 3
@dataclass
class ServiceInstance:
service_name: str
host: str
port: int
metadata: Dict[str, Any] = field(default_factory=dict)
last_heartbeat: float = field(default_factory=time.time)
is_healthy: bool = True
4.2 连接池
import asyncio
import time
from collections import defaultdict
from typing import Optional
from rpc_test.best_rpc.constant import logger
class ConnectionPool:
def __init__(self, max_size=10, idle_timeout=300, connect_timeout=5):
self.max_size = max_size
self.idle_timeout = idle_timeout
self.connect_timeout = connect_timeout
self._pool = defaultdict(list)
self._lock = asyncio.Lock()
async def get_connection(self, host: str, port: int) -> Optional[asyncio.StreamWriter]:
"""获取连接"""
async with self._lock:
key = f'{host}:{port}'
# 1. 首先尝试复用空闲连接
while self._pool[key]:
writer, last_used = self._pool[key].pop()
# 检查连接是否过期
if time.time() - last_used >= self.idle_timeout:
writer.close()
await writer.wait_closed()
continue
# 检查连接是否仍然有效
try:
writer.write(b'\x00') # 发送心跳字节
await asyncio.wait_for(writer.drain(), timeout=1)
return writer
except (ConnectionError, asyncio.TimeoutError) as e:
logger.debug(f"Connection test failed: {e}")
writer.close()
await writer.wait_closed()
continue
# 2. 创建新连接
if len(self._pool[key]) < self.max_size:
for attempt in range(3): # 重试3次
try:
# 添加连接超时
reader, writer = await asyncio.wait_for(
asyncio.open_connection(host, port),
timeout=self.connect_timeout
)
logger.debug(f"Created new connection to {key}")
return writer
except Exception as e:
logger.warning(f"Connection attempt {attempt + 1} failed to {key}: {e}")
if attempt == 2: # 最后一次尝试仍然失败
return None
await asyncio.sleep(0.5) # 等待后重试
return None
async def release_connection(self, host: str, port: int, writer: asyncio.StreamWriter):
"""释放连接回池"""
async with self._lock:
key = f'{host}:{port}'
if writer.is_closing():
return
if len(self._pool[key]) < self.max_size:
self._pool[key].append((writer, time.time()))
else:
writer.close()
await writer.wait_closed()
async def close_all(self):
"""关闭所有连接"""
async with self._lock:
for connections in self._pool.values():
for writer, _ in connections:
if not writer.is_closing():
writer.close()
await writer.wait_closed()
self._pool.clear()
4.3 服务发现与负载均衡
import asyncio
import time
from typing import Callable, Dict, List
from rpc_test.best_rpc.constant import ServiceInstance, logger
from rpc_test.best_rpc.event_bus import EventBus
class ServiceRegistry:
def __init__(self, heartbeat_timeout: int = 30):
self._services: Dict[str, List[ServiceInstance]] = {}
self._event_bus = EventBus()
self.heartbeat_timeout = heartbeat_timeout
self._lock = asyncio.Lock()
async def register(self, instance: ServiceInstance):
"""注册服务"""
async with self._lock:
if instance.service_name not in self._services:
self._services[instance.service_name] = []
# 避免重复注册
existing = next(
(inst for inst in self._services[instance.service_name]
if inst.host == instance.host and inst.port == instance.port),
None
)
if existing:
existing.last_heartbeat = time.time()
existing.is_healthy = True
existing.metadata = instance.metadata
logger.info(f"Updated instance {instance.service_name} at {instance.host}:{instance.port}")
else:
self._services[instance.service_name].append(instance)
logger.info(f"Registered new instance {instance.service_name} at {instance.host}:{instance.port}")
# 通知服务变更
await self._event_bus.publish(
f'service_change:{instance.service_name}',
{'action': 'registered', 'instance': instance}
)
async def deregister(self, service_name: str, host: str, port: int):
"""注销服务实例"""
async with self._lock:
if service_name not in self._services:
return False
removed = False
self._services[service_name] = [
inst for inst in self._services[service_name]
if not (inst.host == host and inst.port == port)
]
if removed:
logger.info(f"Deregistered instance {service_name} at {host}:{port}")
await self._event_bus.publish(
f'service_change:{service_name}',
{'action': 'deregistered', 'host': host, 'port': port}
)
return removed
async def heartbeat(self, service_name: str, host: str, port: int):
"""
处理心跳
:return:
"""
async with self._lock:
if service_name not in self._services:
return False
for inst in self._services[service_name]:
if inst.host == host and inst.port == port:
inst.last_heartbeat = time.time()
if not inst.is_healthy:
inst.is_healthy = True
logger.info(f"Marked instance {service_name} at {host}:{port} as healthy")
await self._event_bus.publish(
f'service_change:{service_name}',
{'action': 'health_changed', 'instance': inst, 'healthy': True}
)
return True
return False
async def discover(self, service_name: str, filter_healthy: bool = True) -> List[ServiceInstance]:
"""发现服务实例"""
async with self._lock:
if service_name not in self._services:
return []
instances = self._services[service_name]
if filter_healthy:
return [inst for inst in instances if inst.is_healthy]
return instances.copy()
async def check_health(self):
"""服务健康检测"""
async with self._lock:
now = time.time()
for service_name, instances in self._services.items():
for instance in instances:
if now - instance.last_heartbeat > self.heartbeat_timeout:
if instance.is_healthy:
instance.is_healthy = False
logger.warning(
f'Instance {service_name} at {instance.host}:{instance.port} is unhealthy'
)
await self._event_bus.publish(
f'service_change:{service_name}',
{'action': 'health_changed', 'instance': instance, 'healthy': False}
)
async def subscribe(self, service_name: str, callback: Callable):
"""订阅服务变更事件"""
await self._event_bus.subscribe(f'service_change:{service_name}', callback)
async def unsubscribe(self, service_name: str, callback: Callable):
"""取消订阅服务变更事件"""
await self._event_bus.unsubscribe(f'service_change:{service_name}', callback)
4.4 熔断器
mport time
from rpc_test.best_rpc.error_enum import CircuitOpenError
class CircuitBreaker:
def __init__(self,
failure_threshold: int = 5,
recovery_timeout: int = 30,
half_open_attempts: int = 2):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.half_open_attempts = half_open_attempts
self._state = 'closed'
self._failure_count = 0
self._last_failure_time = None
self._half_open_success = 0
async def execute(self, func, *args, **kwargs):
"""执行受保护的调用"""
current_time = time.time()
if self._state == 'open':
if current_time - self._last_failure_time > self.recovery_timeout:
self._state = 'half-open'
self._half_open_success = 0
else:
raise CircuitOpenError('Circuit breaker is open')
try:
result = await func(*args, **kwargs)
if self._state == 'half-open':
self._half_open_success += 1
if self._half_open_success >= self.half_open_attempts:
self._state = 'closed'
self._failure_count = 0
return result
except Exception as e:
self._failure_count += 1
if (self._state == 'closed' and self._failure_count >= self.failure_threshold) or (self._state == 'half-open'):
self._state = 'open'
self._last_failure_time =time.time()
raise
4.5 发布订阅
4.5.1 异步事件总线
import asyncio
from collections import defaultdict
from typing import Any, Callable, Dict, List
from rpc_test.best_rpc.constant import logger
class EventBus:
"""异步事件总线"""
def __init__(self):
self._subscribers: Dict[str, List[Callable]] = defaultdict(list)
self._lock = asyncio.Lock()
async def publish(self, event_type: str, data: Any = None):
"""发布事件"""
async with self._lock:
listeners = self._subscribers.get(event_type, [])
logger.debug(f'Publishing {event_type} event to {len(listeners)} listeners')
if listeners:
await asyncio.gather(
*(listener(data) for listener in listeners)
)
async def subscribe(self, event_type: str, callback: Callable):
"""订阅事件"""
async with self._lock:
self._subscribers[event_type].append(callback)
logger.debug(f"New subscriber for {event_type} events")
async def unsubscribe(self, event_type: str, callback: Callable):
"""取消订阅"""
async with self._lock:
if callback in self._subscribers[event_type]:
self._subscribers[event_type].remove(callback)
4.5.2 服务提供者
import asyncio
import os
from typing import Any, Dict
from rpc_test.best_rpc.constant import ServiceInstance, logger
from rpc_test.best_rpc.service_registry import ServiceRegistry
class ServiceProvider:
"""服务提供者"""
def __init__(self, registry: ServiceRegistry, host: str = 'localhost', port: int = 8000):
self.registry = registry
self.host = host
self.port = port
self._services: Dict[str, Any] = {}
self._heartbeat_task = None
self._running = False
def register_service(self, service_name: str, service_instance: Any):
"""注册服务实现"""
self._services[service_name] = service_instance
async def start(self):
"""启动服务提供者"""
if self._running:
return
self._running = True
# 注册所有服务
for service_name in self._services:
instance = ServiceInstance(
service_name=service_name,
host=self.host,
port=self.port,
metadata={'pid': str(os.getpid())}
)
await self.registry.register(instance)
# 启动心跳任务
self._heartbeat_task = asyncio.create_task(self._send_heartbeats())
logger.info(f"Service provider started on {self.host}:{self.port}")
async def stop(self):
"""停止服务提供者"""
if not self._running:
return
self._running = False
if self._heartbeat_task:
self._heartbeat_task.cancel()
try:
await self._heartbeat_task
except asyncio.CancelledError:
pass
# 注销所有服务
for service_name in self._services:
await self.registry.deregister(service_name, self.host, self.port)
logger.info('Service provider stopped')
async def _send_heartbeats(self):
"""定期发送心跳"""
while self._running:
try:
for service_name in self._services:
await self.registry.heartbeat(service_name, self.host, self.port)
except asyncio.CancelledError:
break
except Exception as e:
logger.error(f'Heartbeat failed: {e}')
await asyncio.sleep(1)
4.5.3 服务消费者
import asyncio
from typing import Callable, Dict, List
from rpc_test.best_rpc.constant import ServiceInstance, logger
from rpc_test.best_rpc.service_registry import ServiceRegistry
class ServiceConsumer:
"""服务消费者"""
def __init__(self, registry: ServiceRegistry):
self.registry = registry
self._service_cache: Dict[str, List[ServiceInstance]] = {}
self._callbacks: Dict[str, List[Callable]] = {}
async def discover(self, service_name: str) -> List[ServiceInstance]:
"""发现服务实例并缓存"""
instances = await self.registry.discover(service_name)
self._service_cache[service_name] = instances
return instances
async def watch(self, service_name: str, callback: Callable):
"""监听服务的变更"""
if service_name not in self._callbacks:
self._callbacks[service_name] = []
self._callbacks[service_name].append(callback)
# 初始发现
instances = await self.discover(service_name)
callback({'action': 'init', 'instances': instances})
# 订阅变更
def wrapped_callback(event):
if event['action'] in ['registered', 'deregistered', 'health_changed']:
asyncio.create_task(self._handle_service_change(service_name, event))
self.registry.subscribe(service_name, wrapped_callback)
async def _handle_service_change(self, service_name: str, event: Dict):
"""处理服务变更"""
instances = await self.discover(service_name)
self._service_cache[service_name] = instances
# 通知回调
for callback in self._callbacks.get(service_name, []):
try:
callback({'action': 'update', 'instances': instances, 'event': event})
except Exception as e:
logger.error(f'Callback failed:{e}')
def unwatch(self, service_name: str, callback: Callable = None):
"""取消监听"""
if callback:
if service_name in self._callbacks in self._callbacks[service_name]:
self._callbacks[service_name].remove(callback)
if not self._callbacks[service_name]:
del self._callbacks[service_name]
elif service_name in self._callbacks:
del self._callbacks[service_name]
4.5.4 定义示例服务
class CalculatorService:
def add(self, a, b):
return a + b
async def multiply(self, a, b):
await asyncio.sleep(0.1)
return a * b
class UserService:
async def get_user(self, user_id):
await asyncio.sleep(0.2)
return {"id": user_id, "name": f"user{user_id}"}
4.5.5 健康检查
async def run_health_check(registry: ServiceRegistry, interval: int = 10):
"""定期执行健康检查"""
while True:
try:
await registry.check_health()
await asyncio.sleep(interval)
except asyncio.CancelledError:
break
except Exception as e:
logger.error(f"Health check failed: {e}")
await asyncio.sleep(1)
4.6 执行
4.6.1 服务端实现
async def run_server(host: str, port: int, registry: ServiceRegistry):
"""运行服务提供者"""
provider = ServiceProvider(registry, host, port)
# 注册服务实现
provider.register_service('CalculatorService', CalculatorService())
provider.register_service('UserService', UserService())
await provider.start()
try:
while True:
await asyncio.sleep(1)
except asyncio.CancelledError:
await provider.stop()
4.6.2 客户端实现
async def run_client(registry: ServiceRegistry):
"""运行服务消费者"""
consumer = ServiceConsumer(registry)
# 服务变更回调
def service_changed(event):
action = event['action']
if action == 'init':
print(f'Initial instances: {[f"{i.host}:{i.port}" for i in event["instances"]]}')
elif action == 'update':
print(f'Service changed: {event["event"]["action"]}')
print(f'Current instances: {[f"{i.host}:{i.port}" for i in event["instances"]]}')
# 监听calculatorServicer
await consumer.watch('CalculatorService', service_changed)
# 模拟RPC调用
try:
while True:
instances = await consumer.discover('CalculatorService')
if instances:
instances = instances[0]
print(f'Calling service at {instances.host}:{instances.port}')
else:
print('No available instances')
await asyncio.sleep(3)
except asyncio.CancelledError:
consumer.unwatch('CalculatorService', service_changed)
4.6.3 主函数
async def main():
# 创建共享的注册中心
registry = ServiceRegistry()
# 启动健康检查
health_task = asyncio.create_task(run_health_check(registry))
# 使用Event实现服务就绪通知
service_ready = asyncio.Event()
# 注册服务就绪回调
async def on_service_registered(event):
if event.get("action") == "registered":
service_ready.set()
# 注意这里需要使用 await
await registry.subscribe("CalculatorService", on_service_registered)
# 启动3个服务实例
servers = [
asyncio.create_task(run_server('localhost', 8000 + i, registry))
for i in range(3)
]
# 创建RPC客户端
pool = ConnectionPool(max_size=5)
client = RPCClient(
registry=registry,
pool=pool,
load_balance_strategy=LoadBalanceStrategy.ROUND_ROBIN
)
try:
# 等待服务就绪(带超时)
try:
await asyncio.wait_for(service_ready.wait(), timeout=10)
except asyncio.TimeoutError:
print("警告:等待服务注册超时,可能服务启动失败")
return
# 发起RPC调用
calculator = client.get_proxy("CalculatorService")
add_result = await calculator.add(5, 3)
print(f"[RPC调用结果] 5 + 3 = {add_result}")
# 保持运行
client_task = asyncio.create_task(run_client(registry))
await asyncio.sleep(600)
except KeyboardInterrupt:
pass
finally:
# 取消订阅
await registry.unsubscribe("service_change:CalculatorService", on_service_registered)
# 清理资源
if 'client_task' in locals():
client_task.cancel()
for server in servers:
server.cancel()
health_task.cancel()
await pool.close_all()
await asyncio.gather(
*[t for t in [client_task] if 'client_task' in locals()],
*servers,
health_task,
return_exceptions=True
)
if __name__ == '__main__':
asyncio.run(main())