服务发现

服务化的普及,令软件系统得以通过分布于网络中不同机器的互相协作来复用功能

最早的服务发现可以直接依赖DNS将一个全限定名翻译为一至多个IP地址或者SRV等其他类型的记录便可

但进入微服务时代后,服务宕机 上线下线变得更加频繁 DNS就力不从心了。

服务注册与发现的实现是随zk-eureka-nacos/consul这条线过来的

服务发现原理

sequenceDiagram    participant 服务消费者    participant 注册中心    participant 服务提供者    服务提供者 ->> 注册中心: 注册 (register)    服务提供者 ->> 注册中心: 续约 (renew)    服务提供者 ->> 注册中心: 下载 (cancel)    服务消费者 ->> 注册中心: 获取注册列表 (get registry)    服务消费者 ->> 服务提供者: 发起远程调用

自理式服务发现

应用层:服务直接与服务注册表交互

好处在于可以处理多平台部署问题,弊端则是需要为每种编程语言提供一个SDK。

代理式服务发现

平台层:使用基础设施来实现服务发现

服务发现共性设计

在真实系统中,服务发现中心是整个系统的基础架构 如果它一挂 整个系统就完全崩溃了 所以必须进行高可用支持

sequenceDiagram    participant ServiceProvider1 as Service Provider    participant ServiceDiscovery1 as Service Discovery    participant ServiceDiscovery2 as Service Discovery    participant ServiceDiscovery3 as Service Discovery    participant ServiceConsumer1 as Service Consumer    ServiceProvider1 ->> ServiceDiscovery1: Register / Renew / Cancel    ServiceDiscovery1 ->> ServiceDiscovery2: Replicate    ServiceDiscovery2 ->> ServiceDiscovery3: Replicate    ServiceDiscovery1 ->> ServiceConsumer1: Discovery    ServiceConsumer1 ->> ServiceProvider1: Remote Call

服务发现中心有以Eureka的AP注册中心 也有以Consul为代表的CP注册中心

当然也有AP CP随时转换的Nacos

AP在出现在系统出现网络分区也能继续对外提供服务 不会影响系统操作的正确性场景下 是十分有用的

服务注册中心的实现

AP实现

flowchart TD    subgraph 注册中心        服务注册["服务注册"]        消息回放["消息回放"]    end    subgraph 消息总线        接口1["接口: com.test.Order\n地址: 192.168.1.9:9080\n版本: 2019113589"]        接口2["接口: com.test.Hello\n地址: 192.168.1.2:9080\n版本: 2019103243"]        更多接口["......"]        旧版本接口["接口: com.test.Hello\n地址: 192.168.1.2:9080\n版本: 2019093143"]    end    服务注册 -->|生成注册消息| 接口1    服务注册 -->|生成注册消息| 接口2    服务注册 -->|生成注册消息| 更多接口    消息回放 -->|推拉结合 接受大于本地版本的消息 放弃小于本地版本的消息| 接口1    消息回放 -->|推拉结合 接受大于本地版本的消息 放弃小于本地版本的消息| 接口2    接口1 -->|先入先出\n版本递增| 接口1    接口2 -->|先入先出\n版本递增| 接口2

Eureka

Eureka是Netflix开发的服务发现框架,Eureka包含两个组件: Eureka Server和Eureka Client.

各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息

在应用启动后,将会 向Eureka Server发送心跳,默认周期为30秒

保证AP,eureka在设计时优先保证可用性,每一个节点都是平等的,一部分节点挂掉不会影响到正常节点的工作,不会出现类似zk的选举leader的过程

sequenceDiagram    participant ApplicationClient as ApplicationClient    participant EurekaServer as EurekaServer    participant ApplicationService as ApplicationService    ApplicationService ->> EurekaServer: register    ApplicationService --) EurekaServer: renew    ApplicationClient ->> EurekaServer: 定期获取    ApplicationClient ->> ApplicationService: 调用服务

启动:

  1. 读取和 server 的交互配置信
  2. 读取自身的配置信息,封装
  3. 去 server 端拉取注册信息并缓存到本地
  4. 服务注册
  5. 启动 3 个定时任务

运行:

  1. 定时发送心跳到server端,维持租约
  2. 从server端拉取注册表信息,并更新本地缓存
  3. 监控自身变化,有变动再去注册

销毁:

  1. 把servery端自己的租约销毁掉

服务注册

服务续约

eureka:  instance:    lease-expiration-duration-in-seconds: 10 # 10秒即过期    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳
flowchart TD    A[false]    B{查询租约}    C{是否为unknown}    D[取消租约]    E[更新时间]    F[统计每分钟续约次数,用于自我保护]    B -->|不存在| A    B -->|存在| C    C -->|是| D    C -->|否| E    E --> F

失效剔除与自我保护

有些时候,我们的服务实例并不一定会正常下线,可能由于内存溢出、网络故障等原因使得服务不能正常工作,而服务注册中心并未收到“服务下线”的请求。为了从服务表中将这些无法提供服务的实例剔除,Eureka Server 在启动的时候会创建一个定时任多默认每隔一一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务除出去

默认情况下,EurekaClient会定时向EurekaServer端发送心跳,如果EurekaServer在一定时间内没有收到EurekaClient发送的心跳,便会把该实例从注册服务列表中剔除(默认是90秒),为了防止只是EurekaClient与EurekaServer之间的网络故障,在短时间内丢失大量的实例心跳,这时候EurekaServer会开启自我保护机制,EurekaServer不会踢出这些服务

在开发中,由于会重复重启服务实例,所以经常会出现以下警告:

EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.

所以开发时需要关闭自我保护

eureka:  server:    enable-self-preservation: false # 关闭自我保护模式(缺省为打开)    eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为60*1000ms)

服务下线

Eureka集群

Eureka 满足AP 牺牲了 C

集群同步

zookeeper

保证CP,即任何时刻对zookeeper的访问请求能得到一致性的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务的可用性

Nacos

概念

屏幕截图 2020-09-23 163728

架构

屏幕截图 2020-09-23 163102

vs Zookeeper & Eureka

不同点:

最主要的是Eureka集群中的各个节点是对等的,而Nacos则有主从之分