系统设计心得之服务发现
前言
前段时间参与设计、实现了一个分布式系统,有了一些经验和心得。在接下来一段时间里,我会写一个“系统设计心得”系列,来记录这些经验和心得。主要目的还是对自己思考的一个整理,并不求文章的精确性和完整性。
作为系列的第一篇文章,我们来讨论一下分布式系统中的服务发现问题。这里只讨论各种服务发现方案的选型,而并不会讨论服务发现系统本身的设计和实现。
为了方便讨论,我们约定一下一些名词:
- 业务客户端:指业务、应用自身的客户端。如Web浏览器、RPC Consumer、手机App等。
- Service Client(服务发现客户端):指服务发现自身的客户端,包含了服务发现的相关逻辑。在不引起歧义时简称为“客户端”。
- Service Name(服务名):业务客户端通过服务名来访问服务。
- Service Instance(服务实例):服务提供者的实例,如一个存储服务,或是一个计算服务。
- Service Registry:指保存“服务名到服务实例”映射关系的存储服务。
服务发现概述
服务发现是什么
服务发现要解决的问题是如何通过服务名找到该服务的服务实例:
解决的思路也很直接,就是添加一个Service Registry组件;然后通过某种方式把Service Instance注册到Service Registry;业务客户端在访问时,先查询Service Registry得到一个Service Name对应的Service Instance列表。
服务发现的难点在于服务实例可以动态地被挂载、摘除,客户端要及时感知到这些变化,而不至于影响服务。也就是说,上图中的Service Instance A、B、C是动态变化的。典型的场景有:
- 扩容导致新的服务实例的挂载。客户端要及时感知新实例,从而提升服务能力。
- 缩容导致一些服务实例被摘除。客户端要及时剔除旧实例,从而尽快释放资源。
- 服务实例不可用导致被临时踢出服务。客户端要及时避免把请求再发送给问题实例。
服务发现另外一个功能就是负载均衡。客户端可以根据一定的策略和权重把请求或连接分配到相应的服务实例上。
服务发现的架构模式和对比
服务发现的架构模式
服务发现主要有两种架构模式:客户端服务发现、服务端服务发现。具体的大家可以参考Service Discovery in a Microserivces Architecture。这篇文章对各个服务发现的模式做了详细的介绍,对他们各自的优缺点做了分析。接下来,我会举一些例子来补充一下。
DNS也许是最为熟知、最为广泛的一个客户端服务发现系统。DNS具备客户端服务发现的一些基本特点:
- 服务发现逻辑嵌在业务客户端
- 业务流量不经过服务发现系统
- 服务发现客户端的数量和业务客户端的数量一样多
- 多个业务常常共享同一服务发现服务
LVS则是典型的服务端服务发现系统。它的特点是:
- 业务客户端不感知服务发现逻辑
- 业务流量经过服务发现系统的转发层
- 服务发现客户端的个数有限,只是转发层服务器的个数
- 各个业务的转发层通常是互相隔离的,即有独立的一组转发层
架构对比
客户端服务发现一个很大的优势是业务客户端流量直接打到服务实例上,而不需要经过一个转发层。在延迟、成本上都有一定的优势。客户端服务发现的缺点是每个业务客户端都内嵌了服务发现客户端。大量的服务发现客户端对服务发现服务本身而言是很大的挑战。同时,由于多个业务通常共享一个服务发现服务,一旦服务发现服务出了问题,会有大量的业务受到影响。也就是说客户端服务发现的故障爆炸半径很大。
客户端服务发现一般用在企业内部,这是因为企业内部的协作更为容易。可以为某个业务搭建独立的服务发现服务,从而减轻海量客户端的压力、缩小爆炸半径。可以通过业务发版来对服务发现客户端进行升级。尽管客户端服务发现在企业内部广泛使用,如Dubbo服务,海量客户端、客户端升级、爆炸半径大这些问题还是持续的困扰开发运维人员。
服务端服务发现的优势在于服务发现客户端更为可控,更容易升级而不需要业务方配合。另外一个优势,同时也是缺点,是业务流量经过转发层。这样一来,服务实例和Service Registry之间的健康检查路径和请求路径是一致的。缺点也是显然的,就是多了一层转发层。一方面增加了成本上;另一方面因为转发层在请求路径上,所以对转发层的高可用以及部署有苛刻的要求。
类似于LVS的服务发现在公有云服务中被广泛使用。主要是因为这种架构下业务客户端不用关心服务发现的细节,可以做的更为简单、更为鲁棒。
注册方式对比
服务实例如何注册到Service Registry上也有两种选择,如Service Discovery in a Microserivces Architecture所介绍:自注册(Self-Registration)和第三方注册(Third-Party Registration)。
我们认为第三方注册要优于自注册。因为自注册依赖于服务实例和Registry之间的健康检查,一旦两者发生网络隔离等故障,可能会出现所有的服务实例被全部解注册。在我们看来,注册、解注册路径属于Control Plane,而业务请求路径属于Data Plane。一个服务实例是否可用应该完全取决于Data Plane,即下面两个两个要素:
- 该服务实例是否属于该Service Name
- 该服务实例是否和客户端有健康的连接
“第三方注册”中的“第三方”负责服务实例的注册、解注册,那么它也需要一定的高可用保证。在集中式集群管理中,我们可以用Control Plane的中心服务来完成。
这样我们就解决了上面的第2个问题。第1个问题可以通过在业务客户端和服务实例之间的鉴权来解决。
小结
作为小结,我给出一个企业内部的使用服务发现的设计:
- 采用客户端服务发现架构
- 控制平面的中心服务负责服务实例的加入、退出,并注册、解注册到Service Registry
- 客户端通过健康检查等方式来探活、负载均衡Service Instance
- 客户端和服务实例之间互相校验Service Name,避免一个服务实例的Ip被其他服务复用