转载

微服务治理:服务注册和发现

微服务架构的系统,由多个以业务边界为划分的服务编排或者协同组成。这些服务在独立进程上运行,服务之间通过进程级别的通信机制(rpc、http)进行交互。这就涉及微服务的服务治理问题,首先要解决的是服务注册和服务发现。

需求

最简单的服务注册和发现,需要解决的问题:

  • 一个进程可以对外提供服务,怎么让其他进程知道?
  • 怎么调用其他进程的服务?

这要求有 服务注册中心 ,存储服务列表和路由。

一个服务通常由多个实例进程提供。于是带来新的需求:

  • 提供者:实例上线、下线,要通知注册中心
  • 消费者:这次服务调用要使用哪个实例?(负载均衡)
  • 注册中心:下发服务变化的通知到消费者
  • 注册中心:检查服务提供者是否可用(心跳检查)

当服务提供者和消费者越来越多,对注册中心提出新的需求:

  • 性能
  • 高可用

组件

从上面需求来看,服务注册和发现,至少有以下组件。

  • registry center。存储服务列表和路由信息。
  • discovery。服务提供者暴露服务,服务消费者获取服务列表。
  • health check。心跳检查,服务中心检查服务是否可用。
  • load balance。请求在服务实例之间负载均衡。

服务治理需要哪些信息

在注册、调用之前,先想想服务治理需要哪些信息:

  • 协议。比如http,dubbo,thrift
  • 机房标识。处理跨机房调用,以及同机房优先的策略。
  • 服务地址。可以是ip + host + port描述,也可以是域名。
  • 服务名字。
  • 版本。一个服务可以有多个版本共存。
  • 服务的元数据:方法名,参数列表,返回结果,抛出异常类型,同步/异步响应等
  • 与负载均衡相关的信息,比如超时、限流、权重
  • 服务鉴权。

一种常见的实现方式是,使用URL来描述服务

dubbo://192.168.1.100:20880/com.example.HelloService

http://demo-service/HelloService?version=1

服务注册、服务发现的流程

以dubbo服务注册和发现为例,看看服务注册、服务发现的流程

微服务治理:服务注册和发现

(图片来源: https://dubbo.apache.org/img/architecture.png

  • 注册中心启动
  • 服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者在启动时,向注册中心订阅自己所需的服务。
  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

客户端发现 vs 服务端发现

服务发现有2种模式:客户端发现和服务端发现。上面提到的dubbo框架使用的是客户端发现。Consul、AWS ELB则是服务端发现。

客户端发现,client side discovery

  1. 服务提供者启动后向注册中心登记,服务下线前向注册中心注销。注册中心定时检查服务实例是否在线,并且踢掉异常实例。
  2. 服务消费者从注册中心获取服务地址列表,直接向服务实例发起请求。

微服务治理:服务注册和发现

(图片来源: https://www.nginx.com/wp-content/uploads/2016/04/Richardson-microservices-part4-2_client-side-pattern.png

服务端发现,server side discovery

  1. 服务提供者启动后向注册中心登记,服务下线前向注册中心注销。注册中心定时检查服务实例是否在线,并且踢掉异常实例。
  2. 服务消费者发起的服务调用请求,经过服务路由器、或者负载均衡器处理,由它们去访问注册中心,并且找到服务实例。

微服务治理:服务注册和发现

(图片来源: https://www.nginx.com/wp-content/uploads/2016/04/Richardson-microservices-part4-3_server-side-pattern.png

客户端发现对比服务端发现:

  1. 服务请求路径:client side比server side少了一次网络跳转,效率更高。
  2. 服务发现逻辑:client side模式在服务消费者端实现。对于不同编程语言的服务,要有不同的客户端框架,否则可能无法通信(rpc)。server side由路由器/负载均衡器完成,简化了客户端依赖,但是增加了服务路由器,对基础建设要求更高。

AP or CP

分布式系统逃不开CAP理论。以下摘自wiki

一致性(Consistency) (等同于所有节点访问同一份最新的数据副本)

可用性(Availability)(每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据)

分区容错性(Partition tolerance)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。)

微服务治理:服务注册和发现 (图片来源: https://www.researchgate.net/figure/Visualization-of-CAP-theorem_fig2_282679529

因为网络分区无法避免,因此实际上是CP或者AP的选择。

Eureka是AP设计。节点之间的服务注册信息不做强一致性同步,依赖最终一致性实现节点间注册信息同步。

也有基于zookeeper设计的注册中心。由于zookeeper是强一致性的设计,因此对应的服务中心是CP类型。

那么注册中心应该使用AP还是CP?这就见仁见智。个人认为,对于服务注册这种类型,同一机房使用AP更为合适。

服务的优雅上线、优雅下线

服务的优雅上线实现比较简单,当服务提供者初始化完毕后,再向服务注册中心注册。

实现服务治理框架可以提供接口,判断是否已经完成注册。例如Thrift框架的 Server.isServing() 。 也可以自行对外提供http接口,查询是否已经完成注册。

服务的优雅下线则相对复杂。

如果是正常的主动下线,可以先从注册中心下线,处理完现有请求后,再进行服务自身的下线操作。

考虑非正常的下线情况。

进程被杀掉。如果是 kill -15 ,则会触发应用的关闭钩子,例如java的 addShutdownHook() 。只要在shutdown hook执行下线操作,也是可以顺利主动下线。

如果是 kill -9 ,则不会触发shutdown hook。此时要依赖注册中心进行health check,或者消费者上报调用失败统计,再对该服务实例下线。

消费者接收服务变更通知

注册中心push方式通知消费者,更新及时。但是如果网络瞬间故障,会丢失更新信息。

消费者pull方式主动向注册中心拉取更新,即使单次访问失败,最终也会保持一致。缺点是效率低,接收更新不及时。

因此实际上是推拉结合方式。

服务的心跳检查

通过心跳检查,剔除不在线的服务,从而维护可用服务列表。

服务提供者和注册中心建立health check机制,服务提供者定时发送心跳信息。注册中心的定时任务检查超时未上报服务实例,再根据心跳策略决定是否对失联服务实例下线。

如果服务实例规模相当大,那么心跳机制也会对注册中心产生流量压力。可以考虑其他的通信机制,例如gossip。

注册中心的性能

以下内容复制自“聊聊微服务的服务注册与发现”

对于那些采用了类 Paxos 协议的强一致性的组件,如ZooKeeper,由于每次写操作需要过半的节点确认。水平扩容不能提升整个集群的写性能,只能提升整个集群的读性能。

而对于采用最终一致性的组件来说,水平扩容可以同时提升整个集群的写性能和读性能。

原文  https://ycwu314.github.io/p/micro-service-governance-service-registry-and-discovery/
正文到此结束
Loading...