《深入浅出分布式技术原理》
简介
单体服务架构在早期开发过程中起到了非常重要的作用,但随着业务规模的不断扩大,单体服务的弊端就开始慢慢显现;这时我们就需要往分布式微服务架构转化,将单体服务拆分为一个个微服务,虽然拆分后的架构对公司研发成本、效率和稳定性方面有着非常大的改进,可是在系统运维时(特别是)管理系统配置时却发现效率越来越低了,并且还经常会出现因为配置问题导致的故障。
后端系统由之前单体架构的一个服务被拆分为多个服务后,服务数量还在继续增加;我们管理一个服务的配置还是比较轻松的,可以选择使用环境变量、配置文件等等方式,但是如果使用管理一个服务配置的方式来管理 10 个、20 个甚至更多的服务配置,那么效率一定非常低下(达成共识的难度增加了),而且很容易出错。
我们可以从单服务配置的手动管理这个问题出发,思考为什么需要配置中心?然后进一步讨论配置中心应该具备哪些功能,接着从存储系统选择、配置信息同步这两个方面结合业务场景实际讨论,解决如何实现配置中心的问题,最后再讨论一下:需要配置同时生效的场景下,如何确保配置信息的一致性。
为什么需要配置中心?
在思考配置问题之前,我们应该了解单体服务架构是如何管理配置的:在单体服务架构的场景下,一般将配置信息视为代码的一部分,开发者会像编辑代码一样编辑好配置,然后通过发布系统将配置发布到服务程序所在的机器上,接下来,程序会通过加载本地存储上的配置文件使配置生效。在单体架构下,这个配置即代码的方法能够很好的运行,但是在分布式架构下则会出现几个问题。
- 缺乏统一的配置管理平台,配置管理的效率非常低下;单体服务架构只有一个服务,不需要使用全局视角管理配置,而在分布式系统中,如果将配置信息视为代码的一部分,会导致不同服务的配置文件出现在不同的代码仓库中。当我们需要检索和查看多个服务的配置时,需要在一个个代码仓库中查找,效率会非常低。
- 可能导致实例之间配置不一致;在单体架构下也会出现实例配置不一致的情况,不过整个单体系统只有一个服务,通过人工来保证实例配置一致是比较简单的。但在分布式系统中,随着服务数量的增加,通过人工来提供一致性保证复杂度和难度都太高了。这是因为配置是随程序一起发布的,每一个实例都会加载本地机器上存储的配置信息,如果配置文件被人为误修改或者出现故障,那么就会导致实例之间配置信息的不一致,进而出现各种奇怪的问题。
- 配置即代码的方法会使配置修改的操作变得冗余和低效。每一次配置修改都需要走一次完整的代码发布流程,开发者需要从服务的代码仓库中找到配置文件,在对配置文件修改后,提交修改到代码仓库然后通过发布系统进行发布,最后程序会通过热启动或者热更新的方式加载配置。其中,只有修改配置文件和发布配置文件这两个操作是必须的,其他流程和配置修改无关。
配置即代码的配置管理方式有非常多的问题,那是不是能手动管理配置呢?从操作上看可以,但复杂度也特别高,你需要登录到每一台机器上手动修改,然后让程序重新加载配置文件;即使你十分熟练,保证这个过程没有任何错误,但是假设你的服务配置修改十分频繁,这种方式还可行吗?
所以到底怎么能更高效、更准确的解决分布式系统的配置管理问题呢?
一般来说,在分布式系统中,如果一个问题的影响半径超出单一服务的范围,就可以考虑通过引入一个中间层的方法来解决,即 计算机科学领域的所有问题都可以通过引入一个间接的中间层来解决这个经典论断。
同理,我们在解决上述分布式系统的配置管理问题时,可以通过一个中间层减少状态空间,从而降低问题复杂度,我们把这个中间层称之为配置中心。
引入配置中心这个高效的解决方法后,我们可以进一步讨论一个理想的配置中心应该是什么样子的。
配置中心需要具备哪些功能?
在解决问题之前应该先定义好问题,所以在我们讨论配置中心的具体实现之前,先来定义一下什么是配置中心,换句话说:配置中心应该要具备哪些功能?
结合上下文,配置即代码的方法在分布式系统中面临的三个问题推导出分布式系统架构下,一个理想的配置中心应该具备哪些特点。
- 首先这个配置中心能够统一管理分布式系统所有服务的配置信息。这样研发工程师就可以在配置中心上,便捷地搜索和查看每一个服务的配置信息而不是所有服务的配置信息散落在不同代码库中。更进一步来说:配置中心需要能统一存储和管理整个分布式系统的所有配置文件。
- 配置中心里,同一个服务实例之间的配置应该保持一致。也就是说:配置中心需要保证一个服务所有的实例都加载同一份配置文件而不是每一个实例维护一份自己的配置文件副本。因此需要配置中心统一去管理,服务实例通过网络请求从配置中心获得当前的配置信息,确保单一信任源(Single Source Of Truth,SSOT)。
实际上分布式系统下的问题可以和去中心化的区块链系统类比,我们知道比特币为了实现真正的去中心化发明了 POW 拜占庭共识算法,成功解决了 "double flower question"(双花问题,一笔钱花了多次),这也让比特币获得了巨大的成功(目前单枚比特币价值 42000$,折合人民币 303160.90¥)。
比特币系统采用了 P2P 架构,在全球范围内部署了 7000 多个分布式节点,每个节点上都通过执行 POW 共识算法(挖矿)来维护比特币网络系统的安全性。理论上,一旦一个区块挖出来之后所在的链之后又有 5 个区块挖出,那么这个区块中的交易就被认为是无法回滚的了,此时我们认为比特币系统就这个区块中的上千笔交易达成了共识。从这个例子不难发现,在分布式系统中达成共识的难度比我们想象的要高的多(分布式系统复杂性下限理论),究其原因是因为比特币系统为了实现完全去中心化,抛弃了"领导者"这个角色,这就使状态空间变得非常之大(O(n^2))。
所以如果我们可以保证节点身份可信(诚实节点)的情况下,可以采用强领导者模型减少状态空间,从而更快的达成共识。
综上所述,我们的配置中心显然不属于拜占庭共识范畴(BFT)而是属于(CFT 崩溃容错)范畴,因此可以采用经典的 Raft 作为配置中心节点之间达成一致的共识算法(paladin node 使用了 Raft 算法)。
-
配置中心应该能高效的修改配置。研发工程师只需要关心并且高效的完成配置修改、发布和回滚操作,其他的就不应该由研发工程师费神了,比如配置文件的版本管理等,这些都应该由配置中心自动完成(尽可能减少工程师的使用成本)。
-
配置中心应该支持典型的订阅发布功能。(参考 etcd 的订阅发布实现,比如 Watch 监听与事件通知机制)
经过前面的讨论,我们结合这节课开头提到的配置中心的业务场景,可以总结出配置中心需要解决的关键问题:
- 统一的配置存储:一个带版本管理的存储系统,按服务的维度,存储和管理整个分布式系统的配置信息,这样可以很方便的对服务的配置信息进行搜索、查询和修改。
- 配置信息的同步:所有的实例都能够从配置中心获得服务的配置信息,在配置修改后能够及时将最新的配置同步给服务的每一个实例。
配置中心和服务的注册发现机制是非常类似的,唯一不同的地方是服务注册发现所存储的是服务实例的 IP 和 Port 等信息,是服务实例自己注册的,并且会设置过期时间,随着实例上线时主动写入,下线后会因为过期而被删除。但是配置中心的配置信息是研发工程师主动导入的,并且不会设置过期时间。
如何实现配置中心
关键点
- 统一的配置存储
- 合适的存储系统
- 配置信息的同步
如何选择合适的存储系统
与服务注册发现类似,实现配置中心也需要找一个外部鵆,来做配置中心的统一存储,通过对配置中心的场景分析,可知配置中心对存储系统的要求主要分为以下几点:
- 可用性要求非常高:因为配置中心和服务注册发现一样,是整个分布式系统的基石,如果配置中心出现问题,整个分布式系统将会出现非常严重的问题;
- 性能要求中等:整体性能要求可控,不过随着分布式系统实例的增加,性能要求会有所提高;
- 数据容量要求低:配置中心是用来存储服务的配置信息,一般来说服务的配置信息都非常小,如果出现比较大的配置也不会当成配置来处理,而是放到外部存储上,在配置中放置下载的链接;
- API 友好程度:是否能够很好的支持配置中心场景的 "发布/订阅" 模式,将服务的配置信息实时同步给服务的实例。
mysql、redis 在高可用和 API 友好程度上不满足要求,而 etcd、zookeeper 和 Eureka 这三个存储系统中,更适合的是 Eureka。
为什么 Eureka 这样的 AP 系统要比 etcd(Raft) 和 zookeeper(Paxos) 这样的 CP 系统更加合适呢?
如果我们选择 etcd 和 zookeeper,那么出现网络分区时,在网络分区的少数派一侧配置中心服务将不可用(如果这一侧有实例的重启等操作就会出现故障)。
如果选择 Eureka,那么配置中心这个整体依然可以正常提供服务,主要的问题就变成同一个服务中多个实例之间配置的不一致问题,但这个问题并不是最关键的,主要有两个原因:
- 即使配置中心内部是强一致性的,但是配置中心和服务实例之间是通过网络同步配置的,而网络时延是不确定的,这会导致配置信息同步到实例的时间有先有后,不能同时到达,使得配置中心和同一服务的多实例之间的配置同步退化为最终一致性。
- 配置的修改频率是非常低的,而且因为是人工操作,所以在出现网络分区时,如果我们不去修改配置,那么 Eureka 上多个副本的数据就是一致的。
如何做配置信息的同步?
首先服务实例刚启动时,主动去配置中心拉取完整的配置信息,即首次同步;
服务的每一个实例启动后,通过服务的唯一标识去配置中心获取服务的所有配置,然后加载配置完成实例的启动流程。
然后在实例的运行过程中,如果服务的配置有修改,配置中心需要及时同步到实例,即变更同步:服务的配置信息有变更后,配置中心监听到服务的配置修改了,需要及时通知到服务的所有实例。(可以使用发布订阅或者轮询模式,比如 30s 去配置中心查询一下配置是否有变更)
总结
首先,我们一起讨论了为什么需要配置中心,主要有统一配置管理、同一个服务实例之间的配置一致性和配置修改效率这三个方面的原因。
然后,我们分析了一个理想的配置中心,应该具备什么功能,从中总结出配置中心的两个关键点:统一的配置存储和配置信息的同步。
接着,讨论了对于配置中心的业务场景来说,选择一个 AP 模型的存储系统是最优的方案,并且知道了应该如何做配置信息的同步,
最后,我们通过配置信息需要强一致性的例子,介绍了一个类似两阶段提交的方式,来实现强一致性的配置发布。