心情郁闷而痛苦,发泄来水一发


ZooKeeper与分布式锁

这几天复习+重新学习下ZooKeeper的应用,用原生的Java客户端做了个分布式锁,虽然简单而禁不起推敲,但也感受到了困难与复杂,这个中间件的场景功能比较通用化且偏底层,需要搭配比较偏业务层的框架(比如Curator)来使可能会更好些。

Redis分布式锁的隐患

另外一方面,所谓Redis做分布式锁有问题是指的在主从哨兵模式、Cluster集群模式这样的高可用搭建下,使用分布式锁会出现重复上锁的问题,因为Redis的主从复制是使用的异步方式(追求AP放弃CP),所以如果持锁状态下主节点发生崩溃、这时候锁key还没复制到从节点,那么新的当选主节点上就没有锁了,就有可能会被其他服务给上锁。相当于临界资源没锁住、或者说打破了锁的互斥性,两个服务或者说进程都去操作了临界资源。
如果是Redis单机模式则没有上面的问题,牺牲高可用性来避免了上面的分布式锁的隐患。

"完美"的RedLock算法方案

然后Redis的框架Redisson提供了普通的分布式锁接口,用在Redis单机上,用在主从上会有上面说的隐患。需要仔细评估下是否可以接受这样的小概率错误。
如果要高可用,还要尽量避免上述的重复上锁问题,Redisson提供了所谓RedLock算法实现,基本思路是,启动奇数个独立的Redis节点,相互之间没有主从和集群关系,然后上锁的时候需要向所有节点进行上锁,超过半数成功则视为上锁成功。可见RedLock是个非常重的方案。

或者就直接使用ZooKeeper这样满足CP的中间件了,用的是同步超半数复制,主节点崩了选出来的从节点上的数据也是一致的。不会有上面的隐患。

关于RedLock方案的质疑,可以看Martin Kleppmann的分析,大佬吵架、我等吃瓜就好,另外他写的书《数据密集型应用系统设计》很多好评,有空应该看看。

Consul多数据中心

今天想起来之前一致没去了解的Consul多数据中心的方案,即跨机房的两个由Consul搭建的服务网格如何以服务发现的方式相互调用。这里简单说下:
1、首先就是让两个网格中的Consul server节点之间组成wan gossip池来共享它们之间的节点ip:port,所以当然要想办法实现两个网格中所有的server是互通的,因为跨数据中心的获取服务列表rpc调用是这么一个流程:服务使用者 -> Consul client -> dc1 Consule server -> dc2 Consul server,网格内的server发现是要获取其他数据中心的服务的列表的时候会转发给对应的数据中心的Server(其他数据中心的dc地址已通过wan gossip得到了),这样子最终服务使用者就获取到另一个数据中心的服务的列表了。

2、然后有个问题,上面的获取服务列表调用完成后,服务使用者拿到了另一个dc里某一个服务的服务列表,然后如何调用?
按照一般单dc的思路,这个服务列表里的地址应该都是内网ip,两个数据中心跨网段乃至在互联网上是没法通过内网ip调通的,那么就要想办法、比如开放外网ip,但这显然又不现实,一个数据中心不可能给每个节点都去开放一个外网ip的。我想到的解决办法是按照统一网关入口的思路,可以找一台机器部署一个网关应用、同时这台机器做了内网外网ip的NAT映射,这台机器节点作为整个网格的统一入口,网关应用自己本身注册到网格上、提供这个数据中心的服务的列表(服务名可以特殊标识一下比如dc-xxxService,这样是区分一下dc2内部的xxxService调用),这个dc2外的server转发rpc过来获取dc-xxxService的服务列表就是拿到的这个网关应用的地址列表了。然后dc1内部的服务使用者就可以根据这个网关的地址调用它后面的服务了,因为网关是对dc1是网络通的。