Zab系列9 消息顺序性

Zab系列博客

Raft Vs Zab
https://www.jianshu.com/p/24307e7ca9da
Zab系列1 核心概念
https://www.jianshu.com/p/76e5dba31ea4
Zab系列2 角色和存储
https://www.jianshu.com/p/d80f9250ffd1
Zab系列3 选举
https://www.jianshu.com/p/0d2390c242f6
Zab系列4 zookeeper特性
https://www.jianshu.com/p/08b62ca1fe4e
Zab系列5 选举恢复(源码分析)
https://www.jianshu.com/p/b6acd99921b7
Zab系列6 zk单机版工作原理
https://www.jianshu.com/p/ed45982b18b4
Zab系列7 集群工作原理Leader篇
https://www.jianshu.com/p/59240c36ba1b
Zab系列8 集群工作原理Follower篇
https://www.jianshu.com/p/8d7c7f1b2838
Zab系列9 消息顺序性
https://www.jianshu.com/p/0aa96b6a2070

概括

Zab消息顺序性有两种概念:

  1. 同一个用户发起的两个事务操作,那么后发起的可以覆盖前面一个。比如X-->3,X-->4,那么最终内存树中的结果必须是X=4

  2. 同一个用户发起一个事务操作,一个查询操作,那么如果查询在后面发起的话,那么查询的应该是最新的数据,比如之前X-->3,发起操作 X-->4,查询X,那么X的值应该是4

第一种场景的有序性实现方案:leader按照先后顺序对收到的事务操作分配一个严格自增的zxid,所有的proposal在复制阶段(涉及到持久化,所以比较耗时)不需要遵守严格的顺序,但是在commit的时候,必须严格按照顺序commit,通过加锁实现。执行commit的时候,是单线程处理后续的所有的操作:更新lastProcessZxid、事务命令运用到内存树、反馈客户端响应。从而保证连续事务操作的有序性

第二种场景是根据CommitProcessor.queuedRequests的有序性保证的。所有的请求都会被存到这个队列里面,而且是按照单线程执行,执行到事务操作的时候,需要等待Ack完成。

连续事务操作保证有序性

  1. 事务消息在leader刚收到的时候,会赋予一个自增的 zxid编号。Zab维护就是所有的命令会按照Zxid自增的方式进行commit、Apply、response。
    而且zxid的自增也是可以保证原子性的,类型是AtomicLong
CreateRequest create2Request = new CreateRequest();
pRequest2Txn(request.type, zks.getNextZxid(), request, create2Request, true);

ZookeeperServer.java
private final AtomicLong hzxid = new AtomicLong(0);
    long getNextZxid() {
        return hzxid.incrementAndGet();
    }
  1. 事务消息的处理是放在一个先进先出的有序阻塞队列当中的,挨个处理的

  2. 事务消息的commit有严格的顺序,如果前1个消息没被处理,那么该消息也无法提交,而且这个方法是加锁的,也就是只有前1个消息被彻底commit之后,下一个消息才有机会提交。同时该方法也确保了在toBeApplied中的顺序。也确保了在CommitProcessor.committedRequests的顺序

tryToCommit
  synchronized public boolean tryToCommit(Proposal p, long zxid, SocketAddress followerAddr) {
       if (outstandingProposals.containsKey(zxid - 1)) return false;
        if (!p.hasAllQuorums()) {
           return false;
        }
outstandingProposals.remove(zxid);

        if (p.request != null) {
             toBeApplied.add(p);
        }

            commit(zxid);
            inform(p);
        zk.commitProcessor.commit(p.request);
}
  1. 最最关键是从CommitProcessor开始,后面的ToBeAppliedRequestProcessor、FinalRequestProcessor 都是单线程运行的。而CommitProcessor是从可以保证严格顺序的committedRequests里面捞取已经提交过了的数据的。

这种模式可以确保:所有的Proposal可以按照 Zxid自增的顺序commit,变更lastProcessZxid、运用事务命令到内存树中、顺序反馈给用户操作结果。

你可能感兴趣的:(Zab系列9 消息顺序性)