本文目录导读:

在Python应用中保证最终一致性,主要有以下几种常见方案:
事件驱动架构(Event-Driven)
使用消息队列
# 发布事件
from kombu import Connection, Exchange, Queue
import json
class OrderService:
def create_order(self, order_data):
# 1. 本地事务
order = self.save_to_db(order_data)
# 2. 发布事件
with Connection('amqp://guest:guest@localhost//') as conn:
exchange = Exchange('orders', type='direct')
producer = conn.Producer()
producer.publish(
{'order_id': order.id, 'status': 'created'},
exchange=exchange,
routing_key='order.created'
)
return order
# 消费事件
class InventoryService:
@celery.task
def handle_order_created(event_data):
try:
# 异步处理库存扣减
update_inventory(event_data['order_id'])
except Exception as e:
# 失败重试或发送死信队列
retry_handle.apply_async(args=[event_data], countdown=10)
SAGA 模式实现
Choreography SAGA
class OrderSaga:
def __init__(self):
self.steps = []
self.compensations = []
def add_step(self, action, compensation):
self.steps.append(action)
self.compensations.append(compensation)
def execute(self):
executed = []
for step in self.steps:
try:
result = step()
executed.append(result)
except Exception as e:
# 执行补偿操作
self._rollback(executed)
raise e
return executed
def _rollback(self, executed):
for compensation in reversed(self.compensations[:len(executed)]):
try:
compensation()
except Exception:
# 记录补偿失败到日志
logger.error("Compensation failed")
# 使用示例
saga = OrderSaga()
saga.add_step(
lambda: payment_service.charge(100),
lambda: payment_service.refund(100)
)
saga.add_step(
lambda: inventory_service.reserve('product_1'),
lambda: inventory_service.release('product_1')
)
saga.execute()
基于日志的最终一致性
使用本地消息表
from sqlalchemy import create_engine, Column, String, DateTime, Text
from sqlalchemy.ext.declarative import declarative_base
import datetime
import json
Base = declarative_base()
class MessageOutbox(Base):
__tablename__ = 'message_outbox'
id = Column(String(36), primary_key=True)
topic = Column(String(100))
payload = Column(Text)
status = Column(String(20), default='pending') # pending, delivered, failed
created_at = Column(DateTime, default=datetime.datetime.utcnow)
class ReliablePublisher:
def __init__(self, db_session, broker):
self.db_session = db_session
self.broker = broker
def publish_message(self, topic, payload):
# 1. 保存消息到本地数据库
message = MessageOutbox(
id=str(uuid.uuid4()),
topic=topic,
payload=json.dumps(payload)
)
self.db_session.add(message)
self.db_session.commit()
# 2. 尝试发送消息
try:
self.broker.publish(topic, payload)
message.status = 'delivered'
self.db_session.commit()
except Exception:
# 发送失败,后续由定时任务处理
pass
def retry_failed_messages(self):
# 定时任务:重试失败消息
failed_messages = self.db_session.query(MessageOutbox)\
.filter(MessageOutbox.status == 'pending')\
.all()
for message in failed_messages:
try:
payload = json.loads(message.payload)
self.broker.publish(message.topic, payload)
message.status = 'delivered'
self.db_session.commit()
except Exception as e:
message.status = 'failed'
self.db_session.commit()
基于CDC(Change Data Capture)
使用 Debezium + Kafka
# 业务代码只需更新数据库
class UserService:
def update_user_name(self, user_id, new_name):
# 只需更新数据库,CDC自动捕获变更
user = db.session.query(User).get(user_id)
user.name = new_name
db.session.commit()
# CDC会自动产生事件到Kafka
# 下游服务消费事件
class NotificationService:
def __init__(self):
self.consumer = KafkaConsumer(
'user.change',
bootstrap_servers=['localhost:9092'],
value_deserializer=lambda m: json.loads(m.decode('utf-8'))
)
def start(self):
for message in self.consumer:
if message.value['op'] == 'u': # update操作
new_name = message.value['after']['name']
user_id = message.value['after']['id']
self.send_notification(user_id, f"Name changed to {new_name}")
最佳实践建议
幂等性处理
def process_payment_with_idempotency(payment_id, amount):
# 检查是否已处理
existing = db.session.query(Payment)\
.filter(Payment.idempotency_key == payment_id)\
.first()
if existing:
return existing # 直接返回已有结果
# 执行处理
payment = Payment(
idempotency_key=payment_id,
amount=amount,
status='completed'
)
db.session.add(payment)
db.session.commit()
return payment
补偿机制
import functools
def retry_on_failure(max_retries=3, delay=5):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_retries - 1:
raise
time.sleep(delay * (attempt + 1))
return None
return wrapper
return decorator
@retry_on_failure(max_retries=3, delay=10)
def sync_to_remote_service(data):
response = requests.post('https://remote-service/api/sync', json=data)
response.raise_for_status()
技术选型建议
| 方案 | 适用场景 | 复杂度 |
|---|---|---|
| 消息队列 | 大多数异步场景 | 中 |
| SAGA | 跨服务事务 | 高 |
| 本地消息表 | 单服务内一致性 | 低 |
| CDC | 数据同步、审计 | 中高 |
选择合适的方案需要考虑:
- 业务一致性要求
- 系统规模
- 团队技术能力
- 运维成本
最终一致性的关键是在保证业务正确性的前提下,让系统能够在短暂的不一致状态后自动达到一致状态。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播