本文目录导读:

在Python中实现分布式同步节点分配,主要有以下几种常见方案:
基于Redis的分布式锁
安装依赖
pip install redis
实现代码
import redis
import time
import uuid
from typing import List, Dict
class DistributedNodeAllocator:
def __init__(self, redis_host='localhost', redis_port=6379):
self.redis_client = redis.Redis(
host=redis_host,
port=redis_port,
decode_responses=True
)
self.node_id = str(uuid.uuid4())
def register_node(self, node_name: str, ttl: int = 60):
"""注册节点并获取锁"""
lock_key = f"node_lock:{node_name}"
lock_value = self.node_id
# 尝试获取锁
acquired = self.redis_client.setnx(lock_key, lock_value)
if acquired:
# 设置过期时间
self.redis_client.expire(lock_key, ttl)
print(f"节点 {node_name} 已分配成功(ID: {self.node_id})")
return True
# 检查是否自己的锁
current_owner = self.redis_client.get(lock_key)
if current_owner == self.node_id:
self.redis_client.expire(lock_key, ttl) # 续期
return True
return False
def release_node(self, node_name: str):
"""释放节点"""
lock_key = f"node_lock:{node_name}"
# 使用Lua脚本确保原子性
lua_script = """
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
"""
self.redis_client.eval(lua_script, 1, lock_key, self.node_id)
# 使用示例
allocator = DistributedNodeAllocator()
if allocator.register_node("worker_1"):
# 执行任务
time.sleep(30)
allocator.release_node("worker_1")
基于ZooKeeper的分布式协调
安装依赖
pip install kazoo
实现代码
from kazoo.client import KazooClient
from kazoo.recipe.lock import Lock
import json
class ZKNodeAllocator:
def __init__(self, zk_hosts='localhost:2181'):
self.zk = KazooClient(hosts=zk_hosts)
self.zk.start()
def create_node_group(self, group_name: str, max_nodes: int = 5):
"""创建节点组"""
base_path = f"/node_groups/{group_name}"
# 确保路径存在
self.zk.ensure_path(base_path)
# 创建最大节点数控制
self.zk.ensure_path(f"{base_path}/max_nodes")
self.zk.set(f"{base_path}/max_nodes", str(max_nodes).encode())
def allocate_node(self, group_name: str, node_id: str):
"""分配节点"""
base_path = f"/node_groups/{group_name}"
# 获取当前节点数
nodes = self.zk.get_children(base_path)
max_nodes = int(self.zk.get(f"{base_path}/max_nodes")[0])
if len(nodes) >= max_nodes:
return False
# 创建临时节点
node_path = f"{base_path}/{node_id}"
try:
self.zk.create(
node_path,
value=json.dumps({"status": "active", "timestamp": time.time()}).encode(),
ephemeral=True,
sequence=False
)
return True
except:
return False
def get_active_nodes(self, group_name: str):
"""获取当前活动节点"""
base_path = f"/node_groups/{group_name}"
nodes = self.zk.get_children(base_path)
return [node for node in nodes if node != "max_nodes"]
# 使用示例
allocator = ZKNodeAllocator()
allocator.create_node_group("workers", max_nodes=10)
if allocator.allocate_node("workers", "worker_001"):
print("节点分配成功")
基于gRPC的节点通信
定义protobuf文件 (node_allocator.proto)
syntax = "proto3";
service NodeAllocator {
rpc RegisterNode (NodeRequest) returns (NodeResponse);
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
rpc ReleaseNode (ReleaseRequest) returns (ReleaseResponse);
}
message NodeRequest {
string node_id = 1;
string node_type = 2;
int32 port = 3;
}
message NodeResponse {
bool success = 1;
string message = 2;
string assigned_task = 3;
}
message HeartbeatRequest {
string node_id = 1;
int64 timestamp = 2;
}
message HeartbeatResponse {
bool active = 1;
}
message ReleaseRequest {
string node_id = 1;
}
message ReleaseResponse {
bool success = 1;
}
服务器端实现
import grpc
from concurrent import futures
import node_allocator_pb2
import node_allocator_pb2_grpc
import threading
class NodeAllocatorService(node_allocator_pb2_grpc.NodeAllocatorServicer):
def __init__(self):
self.nodes = {}
self.lock = threading.Lock()
self.heartbeat_timeout = 30 # 秒
def RegisterNode(self, request, context):
with self.lock:
node_id = request.node_id
if node_id in self.nodes:
return node_allocator_pb2.NodeResponse(
success=False,
message="Node already registered"
)
self.nodes[node_id] = {
'type': request.node_type,
'port': request.port,
'last_heartbeat': time.time()
}
return node_allocator_pb2.NodeResponse(
success=True,
message="Node registered successfully",
assigned_task="task_001"
)
def Heartbeat(self, request, context):
node_id = request.node_id
if node_id in self.nodes:
self.nodes[node_id]['last_heartbeat'] = time.time()
return node_allocator_pb2.HeartbeatResponse(active=True)
return node_allocator_pb2.HeartbeatResponse(active=False)
def check_stale_nodes(self):
"""检查并清理过期节点"""
while True:
time.sleep(10)
current_time = time.time()
with self.lock:
stale_nodes = [
node_id for node_id, info in self.nodes.items()
if current_time - info['last_heartbeat'] > self.heartbeat_timeout
]
for node_id in stale_nodes:
del self.nodes[node_id]
print(f"Node {node_id} removed (timeout)")
# 启动服务器
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
service = NodeAllocatorService()
node_allocator_pb2_grpc.add_NodeAllocatorServicer_to_server(service, server)
server.add_insecure_port('[::]:50051')
server.start()
# 启动心跳检查线程
threading.Thread(target=service.check_stale_nodes, daemon=True).start()
print("Server started on port 50051")
server.wait_for_termination()
if __name__ == '__main__':
serve()
客户端实现
import grpc
import node_allocator_pb2
import node_allocator_pb2_grpc
import time
import uuid
class NodeClient:
def __init__(self, server_address='localhost:50051'):
self.channel = grpc.insecure_channel(server_address)
self.stub = node_allocator_pb2_grpc.NodeAllocatorStub(self.channel)
self.node_id = str(uuid.uuid4())
def register(self, node_type='worker', port=8080):
request = node_allocator_pb2.NodeRequest(
node_id=self.node_id,
node_type=node_type,
port=port
)
try:
response = self.stub.RegisterNode(request)
return response.success, response.message, response.assigned_task
except grpc.RpcError as e:
return False, str(e), None
def send_heartbeat(self):
request = node_allocator_pb2.HeartbeatRequest(
node_id=self.node_id,
timestamp=int(time.time())
)
try:
response = self.stub.Heartbeat(request)
return response.active
except:
return False
def run(self):
# 注册节点
success, message, task = self.register()
if success:
print(f"Node {self.node_id} registered, assigned task: {task}")
# 定期发送心跳
while True:
time.sleep(10)
if not self.send_heartbeat():
print("Heartbeat failed, trying to reconnect...")
self.register()
# 使用客户端
client = NodeClient()
client.run()
基于消息队列的方案 (RabbitMQ)
安装依赖
pip install pika
实现代码
import pika
import json
import uuid
import threading
class RabbitMQNodeAllocator:
def __init__(self, host='localhost'):
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(host)
)
self.channel = self.connection.channel()
self.node_id = str(uuid.uuid4())
# 声明交换机和队列
self.channel.exchange_declare(
exchange='node_allocator',
exchange_type='direct'
)
# 每个节点监听自己的专用队列
result = self.channel.queue_declare(queue=self.node_id, exclusive=True)
self.queue_name = result.method.queue
self.channel.queue_bind(
exchange='node_allocator',
queue=self.queue_name,
routing_key=self.node_id
)
def request_node_assignment(self, task_type: str):
"""请求节点分配"""
request = {
'action': 'request_assignment',
'node_id': self.node_id,
'task_type': task_type,
'callback_queue': self.queue_name
}
# 发送请求到分配器
self.channel.basic_publish(
exchange='',
routing_key='allocator_queue',
body=json.dumps(request)
)
# 等待响应
def callback(ch, method, properties, body):
response = json.loads(body)
if response['node_id'] == self.node_id:
print(f"Node assigned: {response['assigned_node']}")
ch.stop_consuming()
self.channel.basic_consume(
queue=self.queue_name,
on_message_callback=callback,
auto_ack=True
)
self.channel.start_consuming()
最佳实践建议
-
选择合适的方案:
- 小规模系统:Redis方案足够
- 需要强一致性:ZooKeeper
- 需要自定义协议:gRPC
- 异步通信:消息队列
-
处理异常情况:
- 实现心跳机制检测节点存活
- 设置超时时间自动释放资源
- 实现重试机制
-
安全性考虑:
- 使用SSL/TLS加密通信
- 实现身份验证
- 限制节点访问权限
-
性能优化:
- 使用连接池管理网络连接
- 实现批量操作减少网络开销
- 使用异步IO提高吞吐量
这些方案都可以根据具体需求进行定制,选择最适合你项目的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播