我们设计的分布式系统,在正常工作时呈现出网状。服务有层次性,客户的请求会逐步经历各层服务进行处理,当遍历完所有服务后才会完成一次请求。每层服务会有若干台机器,上游节点的机器可以把输出结果传递到下游节点的任意一台机器上。
当服务所依赖的数据需要更新时,我们需要做好同步工作,并保证在数据更新过程中服务是可用的。这儿介绍两类更新的操作方式,它们都需要用到zookeeper来实现。
第一类更新只局限于一个服务的所有机器。我们需要给它们的更新设定一个顺序,避免出现该服务所有机器同时更新这种极端情况。Zookeeper鼓励使用异步的api进行编程,在实现过程中至少有两种方式来实现这种分布式锁。第一种是试图去创建一个既定的结点,如果成功则表示锁已经拿到,可以开始更新,否则创建观察点,等待别的机器完成更新后释放这个锁(当然仍然可能拿不到);第二种则去创建一个顺序节点(sequence),zookeeper能保证创建节点的唯一性,然后服务只需要监控顺序在自己之前的节点是否完成了更新(释放锁)。当数据量不大时,两种实现方式的性能应该差别不大,数据量大时推荐使用第二种方式,因为它可以降低通知时产生的网络流量。第一种方式在抢锁过程中,网络更快的机器更容易抢到,第二种方式是基于排队机制的。虽然逻辑简单,但实际编程过程中还是会比较麻烦,需要考虑网络传输等问题等。
第二类更新是多个服务中有数据依赖的情况。比如服务A和B,它们均有两台机器a1, a2, b1, b2。初始时的数据时间戳相同,即服务A的数据是da_t1, 服务B的数据是db_t1。如果我们有了新的数据da_t2和db_t2,我们只允许首先同时更新A和B的其中一台机器,如a1, b1;这是更新数据后的服务器也只会把自己的下游数据发送到已经更新数据的下游服务器中去,即由原先的a1和a2都可以把下游数据发送给b1和b2的任意一台,现在a1只能给b1,a2只能给b2。直到a2,
b2均更新为新数据的时候,才能恢复原先的传输方式。具体的实施方式是首先我们需要知道整个数据更新的总服务器的情况, 以数据的名来命名(index),如下: /data/index/A/a1, /data/index/A/a2, /data/index/B/b1,/data/index/B/b2, 一旦时间戳为t2的数据包都准备好了,那么修改节点/data/index的值为t2(可以一级一级的更新,即/data/index/A, /data/index/B的时间戳都修改为t2后,再修改/data/index),并且选取每类服务的若干机器开始更新,即增加/updating/index/A/a1,
/updating/index/B/b1,并赋值为t2。每台服务器会监控自己所属的结点,即a1会监控/updating/index/A/a1,一旦发现该节点出现,就开始进行数据更新,更新完成后会删除自己的所属节点。监控服务一旦发现/updating/index/A/a1, /updating/index/B/b1均被删除了,就会重新赋值/updating/index/A/a2,/updating/index/B/b2。当时间戳为t2的所有数据均完成更新后,对/updated/index进行赋值为t2。但是在实际编程过程中,完全按照上面的描述进行会非常的麻烦,所以我们还是进行了简化,但其逻辑还是得以保证。
至此,对于我设计的分布式框架系统已经介绍完毕。