分布式锁-session-事务相关

本文详细介绍了分布式锁、分布式session、分布式事务及分布式日志的关键技术和实现原理。以Zookeeper实现的分布式锁为例,通过创建有序临时节点,判断节点序号来决定锁的归属。Redis用于实现分布式session管理,通过非粘滞性模式保证了session的一致性。通过消息队列避免了分布式事务带来的复杂性,并介绍了两阶段提交协议的工作流程。最后,文章还探讨了分布式日志的采集和动态修改日志级别的方法。

1. 分布式锁--zookeeper

1). client调用create()方法创建“/root/lock_”节点,注意节点类型是EPHEMERAL_SEQUENTIAL。
2). client调用getChildren("/root/lock_",false)来获取所有已经创建的子节点,这里并不注册任何Watcher。
3). 客户端获取到所有子节点Path后,如果发现自己在步骤1中创建的节点是所有节点中最小的,那么就认为这个客户端获得了锁。
4). 如果在步骤3中,发现不是最小的,那么找到比自己小的那个节点,然后对其调用exist()方法,并注册事件监听移除事件。
5). 之后一旦这个被关注的节点移除,客户端会收到相应的通知,这个时候客户端需要再次调用getChildren("/root/lock_",false)来确保自己是最小的节点,然后进入步骤3.

2. 分布式session--redis

1). 将session已sessionId作为key,保存缓存集群中.
2). 请求到来时从缓存中加载session,出来后写回.

//tomcat基于redis
    使用功能tomcat-redis-session-manager, 设置non-sticky模式,每次映射到后端的server是不同的,当请求到来时,
从redis中恢复session,处理完成后session再写回redis.

3. 分布式事务-消息队列来避免分布式事务

3.1 消息队列来避免分布式事务

    当支付宝账户扣除1万后,我们只要生成一个凭证(消息)即可,这个凭证(消息)上写着“让余额宝账户增加 
1万”,只要这个凭证(消息)能可靠保存,我们最终是可以拿着这个凭证(消息)让余额宝账户增加1万的,即我们能
依靠这个凭证(消息)完成最终一致性。

3.2 两阶段提交协议--多次通信,性能太差

缺点:
    1)两阶段提交涉及多次节点间的网络通信,通信时间太长!
    2)事务时间相对于变长了,锁定的资源的时间也变长了,造成资源等待时间也增加好多!

过程--数据库前需要一个TC(事务协调器)
    1) 我们的应用程序(client)发起一个开始请求到TC;
    2) TC先将<prepare>消息写到本地日志,之后向所有的Si发起<prepare>消息。以支付宝转账到余额宝为例,TC给A的prepare消息是通
        知支付宝数据库相应账目扣款1万,TC给B的prepare消息是通知余额宝数据库相应账目增加1w。为什么在执行任务前需要先写本地日志,
        主要是为了故障后恢复用,本地日志起到现实生活中凭证 的效果,如果没有本地日志(凭证),出问题容易死无对证;
    3) Si收到<prepare>消息后,执行具体本机事务,但不会进行commit,如果成功返回<yes>,不成功返回<no>。同理,返回前都应把要返回的消息写到日志里,当作凭证。
    4) TC收集所有执行器返回的消息,如果所有执行器都返回yes,那么给所有执行器发生送commit消息,执行器收到commit后执行本地事务的commit操作;
        如果有任一个执行器返回no,那么给所有执行器发送abort消息,执行器收到abort消息后执行事务abort操作。

注:TC或Si把发送或接收到的消息先写到日志里,主要是为了故障后恢复用。如某一Si从故障中恢复后,先检查本机的日志,如果已收到<commit >,则提交,如果<abort >则回滚。如果是<yes>,则再向TC询问一下,确定下一步。如果什么都没有,则很可能在<prepare>阶段Si就崩溃了,因此需要回滚。

4. 分布式日志

4.1 问题定位-采集日志

统一的日志web端 --> 输入日志关键字,一般opeId --> 点击开始 --> 办理业务 --> 点击结束 --> 后台去各个业务节点日志中按起止时间+oprId过滤 
    -->汇总日志返回 --> 保存分析即可  

4.2 动态修改日志级别

转载于:https://www.cnblogs.com/Desneo/p/7209492.html

### 分布式事务 分布式事务是指在分布式系统环境中,由不同服务之间通过网络远程协作来完成的一系列操作。这些操作要么全部成功,要么全部失败并回滚[^2]。 #### 实现机制 对于跨库的情况,即在一个服务内涉及多个数据源的操作,由于本地事务无法跨越多个数据库实例,因此需要采用两阶段提交协议(2PC)、三阶段提交协议(3PC),或是基于消息队列的最终一致性方案等方法来保证事务的原子性和隔离性[^4]。 当涉及到多个微服务之间的交互时,则可以利用TCC模式(Try-Confirm-Cancel)、Saga编排模型以及事件驱动架构等方式处理复杂的业务流程,并确保整个过程满足ACID特性中的某些方面或达到BASE理论的要求。 ```python from sqlalchemy import create_engine, Column, Integer, String, ForeignKey from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship Base = declarative_base() class Order(Base): __tablename__ = 'orders' id = Column(Integer, primary_key=True) user_id = Column(String(50)) product_ids = relationship('Product', secondary='order_products') class Product(Base): __tablename__ = 'products' id = Column(Integer, primary_key=True) name = Column(String) association_table = Table( "order_products", Base.metadata, Column("order_id", ForeignKey("orders.id"), primary_key=True), Column("product_id", ForeignKey("products.id"), primary_key=True) ) engine = create_engine('sqlite:///example.db') Session = sessionmaker(bind=engine) session = Session() try: with session.begin(): order = Order(user_id="u1") products = [Product(id=i) for i in range(1, 4)] order.product_ids.extend(products) session.add(order) except Exception as e: print(f"Transaction failed due to {e}") finally: session.close() ``` 此代码片段展示了如何使用SQLAlchemy ORM框架管理关系型数据库中订单表和商品表间的关系,在同一个会话上下文中执行多项更改以模拟简单的分布式事务行为。请注意实际生产环境下的实现可能更加复杂,需考虑更多因素如并发控制、死锁预防等。 --- ### 分布式锁 为了防止在同一时间点上有超过一个客户端能够修改共享资源而导致的数据不一致问题,可以在分布式应用程序里引入锁定机制——这就是所谓的分布式锁[^1]。 #### 实现方式 常见的几种实现途径如下: * **基于数据库**:利用唯一索引约束或者乐观/悲观锁策略; * **Redis缓存**:借助SETNX命令设置键值对作为标志位,配合EXPIRE设定过期时间防止单点故障引发的永久占用现象; * **Zookeeper/Etcd**:依靠临时节点创建竞争选举领导者角色达成互斥访问目的。 下面给出一段Python伪代码用于说明基于Redis实现简单版本的分布式锁逻辑: ```python import redis import time import uuid def acquire_lock(redis_client: redis.Redis, lock_name: str, timeout_ms: int) -> bool: identifier = str(uuid.uuid4()) end_time = time.time() + (timeout_ms / 1000.0) while time.time() < end_time: if redis_client.set(lock_name, identifier, nx=True, ex=int(timeout_ms)): return True time.sleep(.001) return False def release_lock(redis_client: redis.Redis, lock_name: str, identifier: str) -> None: pipe = redis_client.pipeline(True) try: while True: pipe.watch(lock_name) if pipe.get(lock_name).decode() != identifier: break pipe.multi() pipe.delete(lock_name) pipe.execute() break except redis.WatchError: pass redis_conn = redis.StrictRedis(host='localhost', port=6379, db=0) if acquire_lock(redis_conn, "my_distributed_lock", 10 * 1000): # 尝试获取名为'my_distributed_lock' 的锁对象,超时时限设为10秒 try: # 执行受保护的关键区段内的敏感操作... ... finally: release_lock(redis_conn, "my_distributed_lock") # 不论是否发生异常都应释放所持有的锁 else: raise RuntimeError("Failed to obtain distributed lock.") ``` 上述例子中定义了`acquire_lock()`函数尝试获得指定名称的锁直到给定的时间期限到达为止;而`release_lock()`则负责安全地移除已存在的锁标记以便其他等待者继续前进。 --- ### 应用场景比较 | 特征 | 分布式事务 | 分布式锁 | | --- | --- | --- | | 主要作用范围 | 多个服务间的协调工作,特别是在存在依赖关系的情况下保障整体操作的成功与否 | 协同多台服务器上的程序单元有序地访问公共资源而不造成冲突 | | 关注重点 | 数据一致性维护,特别是面对部分成员失败后的恢复能力 | 并发控制与同步,减少竞态条件带来的不确定性影响 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值