Service Discovery_系统设计笔记7

一.应用层

在简单的 3 层结构中,Web 服务层既要处理请求,又要承载业务功能:

而更优的结构是把 Web 层与应用层(也叫平台层)分开:

优势在于:

  • 能够单独扩展应用层:允许独立加机器、换专用机器等

  • 复用基础设施:简化多端支持,缓存、数据库等处理都可以复用

  • 使得组织更容易扩展:一个团队负责实现/优化平台本身,其它多个团队利用平台功能进行开发

分离出应用层之后,面临的下一个问题是应用层内部如何划分职责,如何协同工作,也就是微服务架构所要解决的问题

二.微服务架构

微服务架构提倡把应用程序设计成一系列松耦合的细粒度服务,并通过轻量级的通信协议组织起来:

In a microservices architecture, an application is structured as a collection of loosely coupled services, which are fine-grained and the protocols are lightweight.

这些服务都能够独立部署、独立扩展,每个服务都具有稳固的模块边界,甚至允许使用不同的编程语言来编写不同服务,也可以由不同的团队来管理:

P.S.关于微服务架构的更多信息,见微服务架构(Microservices)

微服务架构下,应用被拆分成了多个服务,各自运行在(不同机器的)不同进程中:

如果每个微服务都只运行在单台机器上,一个微服务可以通过静态配置表找到其它依赖服务,进而通过服务间通信完成协作

然而,实际场景下,1 个微服务通常会部署在多台机器上,并按需动态伸缩(增减机器),简单的静态配置显然无法满足,因而需要一种服务注册查询机制

即 Service Discovery

三.Service Discovery

客户端 Service Discovery

客户端查询服务注册表,得到目标服务的一系列地址,并根据负载均衡策略从中选择一个发起请求(即客户端负载均衡

其中,服务注册表(service registry)用来存放所有可用的服务实例,并提供管理(注册/注销)和查询 API:

The service registry is a database of available service instances. The service registry provides a management API and a query API. Service instances are registered with and deregistered from the service registry using the management API. The query API is used by system components to discover available service instances.

具体的,在启动服务实例时,向注册表添加其网络位置,停掉服务时移除记录,并在服务实例运行期间,通过心跳机制周期性地刷新注册信息

P.S.例如Netflix Eureka提供了 REST API 用来注册/注销、查询服务实例:

Netflix Eureka provides a REST API for registering and querying service instances. A service instance registers its network location using a POST request. Every 30 seconds it must refresh its registration using a PUT request. A registration is removed by either using an HTTP DELETE request or by the instance registration timing out. As you might expect, a client can retrieve the registered service instances by using an HTTP GET request.

以及配套的Netflix Ribbon,用作客户端负载均衡

这种模式相对简单,而且客户端能够做出更聪明的(比如特定于应用程序的)负载均衡决策,但也存在一些缺点:

  • 客户端用到的每种语言都要实现一遍

  • 需要自行维护一个高可用的注册服务

  • 服务发现相关逻辑都在客户端实现,比如重试,造成客户端比较重

DNS-SD

特殊的,可以将 DNS 用作服务注册表,称之为DNS-SD(DNS-based Service Discovery)

通过 DNS SRV 记录来完成服务到实例的一对多映射:

SRV 记录(Service locator record):通用服务定位记录,指定服务所在的服务器(域名和端口号),多用于 SIP(Session Initiation Protocol,会话发起协议)

(摘自资源记录 | DNS_系统设计笔记 3

例如:

$ nslookup -query=SRV _http._tcp.backends.example.com 10.0.0.2
Server:     10.0.0.2
Address:    10.0.0.2#53

_http._tcp.backends.example.com service = 0 2 8090 backend-0.example.com.
_http._tcp.backends.example.com service = 0 1 8091 backend-1.example.com.
_http._tcp.backends.example.com service = 10 1 8092 backend-2.example.com.

借助 DNS 虽然简单易操作,但受限于 DNS 的更新时效(缓存问题)

服务端 Service Discovery

当然,查询的过程也可以在服务端完成:

客户端通过负载均衡器请求目标服务,负载均衡器查注册表得到一组可用实例,并根据负载均衡策略从中选择一个发起请求

P.S.例如AWS Elastic Load Balancer (ELB)

这种模式下,客户端不必再为各种语言、不同框架实现服务查询逻辑,简单地向负载均衡器发起请求即可,但如果部署平台没有提供这种能力的话,需要自行建立并维护这样一个高可用的系统组件

四.服务注册与注销

Service Discovery 中,服务实例必须注册到服务注册表,并及时注销,分为自注册与第三方注册 2 种模式

自注册模式

自注册模式下,服务实例负责把自己注册到服务注册表,以及从中注销,必要的话,还要发送心跳请求保持活跃,避免其注册过期

这种方式相对简单,不依赖其它系统组件,但服务实例和服务注册机制产生了耦合,以致于注册逻辑需要在各种语言、不同框架的客户端都实现一遍

P.S.Netflix OSS Eureka client采用的就是这种模式,由 Eureka 客户端来处理服务实例的注册和注销

第三方注册模式

服务实例不再负责注册/注销,交由服务登记员(service registrar)来处理,解除了服务实例与注册机制间的耦合关系。登记员通过轮询部署平台或订阅事件来跟踪服务实例的运行状态,发现新服务实例就注册上去,发现服务实例停掉了就注销掉

P.S.Registrator采用了这种模式,支持自动注册/注销用 Docker 容器部署的服务

特殊的,部署平台掌控着服务实例的启动与停止,由它来完成注册、注销再合适不过了。事实上,KubernetesMarathon等部署平台也都提供了服务注册、查询的能力。具体的,把集群中运行在每个节点上的代理服务用作服务端 Service Discovery 里的负载均衡器,客户端向代理发送请求,由代理服务转发给集群中其它节点上的可用实例

五.总结

微服务架构负责拆分服务、解耦依赖关系,而 Service Discovery 用来解决这些服务间的通信问题,让一个微服务能够找到另一个

实现上,分为客户端 Service Discovery 与服务端 Service Discovery 两种,区别在于查询/选取逻辑实现在客户端还是服务端。而服务的注册/注销可以由服务自身完成(自注册),也可以由部署平台等第三方来完成(第三方注册)

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code