五、Spring Cloud Netflix Eureka 注册中心
官网地址 Spring-cloud-Netflix-Eureka-clients Spring-Cloud-Neflix-Eureka-server
Spring Clud Netflix Eureka : 作为微服务的注册中心,动态感知服务的上下线
spring-cloud-starter-netflix-eureka-client、 spring-cloud-starter-netflix-eureka-server
一、Eureka简单使用
在使用Ribbon和OpenFeign来实现集群的负载均衡和面向接口调用时,当用户服务调用订单服务集群时,需要在用户服务内配置订单服务集群的地址列表,这样才能达到客户端负载均衡的效果。也就是说需要在客户端配置文件中维护服务提供者的列表。
# 配置指定服务的提供者的地址列表
shen-order-service.ribbon.listOfServers=\
localhost:8081,localhost:8082
1. 在客户端配置文件中维护服务提供者的列表会带来的问题
- 不能动态感知服务上下线,节点宕机请求一直异常
- 服务调用者的维护成本 (100个节点的集群,每个服务调用者都需要维护100个地址)
为了解决这些问题,我们可以引入服务注册中心:
服务提供者将自身节点以 [key(服务名称) : value(服务地址)]形式注册到注册中心,而服务调用者从注册中心获取key为服务提供者名称的地址列表即可;这样就解决了服务调用者的维护成本;
而对于服务的动态上下线感知,可以通过心跳检测heartBeat来检查服务提供者的状态,当注册中心定时发送心跳至服务提供者集群节点时,当某一节点超时无响应,注册中心可以将该节点剔除,实现服务的动态下线感知;
当注册中心的服务地址列表发生变化时,怎么通知到服务调用者呢?一般有两种方式,pull和push。
服务动态上下线感知
下线: 心跳检测,剔除无响应的服务
注册中心push: 定时推送,需要维护大量会话;socket监听
调用者pull: 定时拉取,不需要维护大量回话,数据会有延迟; http协议,本身也是Socket
![](https://img2020.cnblogs.com/blog/1660657/202007/1660657-20200720205916637-24081403.png)
常用的服务注册中心:Zookeeper
、Eureka
、Redis
、Consul
等
几种注册中心的差异可以从以下几种特性来比较:
推送发送方式: push/pull
存储: 是否支持持久化
高可用机制: 集群特性、选举特性、一致性问题
CAP特性: 一致性、可用性、分区容错性; 满足CP还是AP
API的提供形式 http协议、neety通信
2. Spring Cloud Eureka 实现注册中心
a. 注册中心 Eureka-server
- 新建SpringBoot项目,添加Eureka Server服务依赖
Spring Cloud Discovery
->Eureka-Server
:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
- 开启注册中心 @EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudEurekaServerApplication {
}
- 维护配置文件
application.properties
spring.application.name=spring-cloud-eureka-server
# 应用服务 WEB 访问端口
server.port=9090
# 指向服务注册中心的地址 自己作为服务去注册
eureka.client.service-url.defaultZone=http://localhost:9090/eureka
## 不注册自己为服务
#eureka.client.register-with-eureka=false
#eureka.client.fetch-registry=false
之后,一个简易版的服务注册中心就配置好了。服务端口为9090,本地启动直接访问 http://localhost:9090/ 可以打开注册中心页面,查看已经注册到该注册中心的服务。当服务启动时,默认会将自身作为服务注册,所以打开时会有自身服务SPRING-CLOUD-EUREKA-SERVER
b. 服务提供者 order-service
order-service作为Eureka-Client,维护Eureka-Server地址来注册
- 添加Eureka Client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
- 维护server地址来做服务注册
spring.application.name=shen-order-service
eureka.client.service-url.defaultZone=http://localhost:9090/eureka
配置好后启动两个order service服务,可以从 http://localhost:9090/ 页面查看到两个订单服务已经注册进Eureka Server
c. 服务调用者 user-service
服务调用者也作为 Eureka-Client注册到Eureka-Server上。
- 添加Eureka-Client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
- 配置文件维护Eureka Server地址
此时可以将之前需要在配置文件内维护的shen-order-service.ribbon.listOfServers
服务提供者地址列表删除,改由注册中心获取
spring.application.name=shen-user-server
# 配置指定服务的提供者的地址列表
#shen-order-service.ribbon.listOfServers=\
# localhost:8081,localhost:8082
logging.level.com.bigshen.springcloud.demo.client=DEBUG
eureka.client.service-url.defaultZone=http://localhost:9090/eureka
![](https://img2020.cnblogs.com/blog/1660657/202007/1660657-20200720213738387-754804642.png)
而原有的用户服务调用订单服务的业务接口也是可以负载均衡调用成功的。
3. Eureka 注册中心的高可用 (集群)
![](https://img2020.cnblogs.com/blog/1660657/202007/1660657-20200720215125120-1632446994.png)
- Eureka Server互为备份,没有主从概念,去中心化
- Eureka Replicate 来数据同步,Eureka Server集群是一个AP模型,保证高可用及分区容错性,不保证强一致性而是最终一致性
- 服务提供者Application Server作为1个Eureka Client,对应1个Eureka Server节点,可以
Register
注册服务;Renew
续约;Cancel
下线;Get Registry
获取服务地址列表 - 服务调用者Application Client作为1个Eureka Client,可以从Eureka Server中
Get Registry
获取服务地址列表,发起远程调用。
a. 创建注册中心集群
重复之前创建Eureka Server的步骤,创建erueka-server02,端口为9091,然后两个节点相互注册:
如原先节点为9090节点,将其注册至9091节点:
# 应用服务 WEB 访问端口
server.port=9090
# 指向服务注册中心的地址 自己作为服务去注册
eureka.client.service-url.defaultZone=http://localhost:9091/eureka
9091节点注册到9090节点,两者相互注册
# 应用服务 WEB 访问端口
server.port=9091
# 指向服务注册中心的地址 自己作为服务去注册
eureka.client.service-url.defaultZone=http://localhost:9090/eureka
b. order-service和user-service维护注册中心地址
order-service和user-service作为Eureka Client,配置文件中更改注册中心的地址:
eureka.client.service-url.defaultZone=http://localhost:9090/eureka,http://localhost:9091/eureka
本次例子地址 Spring-Cloud-Netflix-Eureka
二、Eureka特性分析
1. Eureka的自我保护机制
心跳失败的比例在15分钟之内,低于85%的节点,Eureka Server会认为这个实例出现了网络故障。(也就是大部分请求都能正常访问,偶尔访问不到,Eureka会认为可能是Eureka和服务之间的网络故障,而不是服务提供者本身服务的问题)
减少网络抖动或者网络不稳定的情况下,避免误删除。这样的话这个服务就不会被剔除,仍然保留在Eureka中。
- Eureka Server不会剔除因为长时间没有收到心跳数据的过期服务
- Eureka Server仍然能够接受新的服务的注册和查询
Eureka自我保护机制的实现逻辑
在Eureka-Server服务依赖内 com.netflix.eureka.registry.AbstractInstanceRegistry
类,提供了自我保护机制的实现:
class com.netflix.eureka.registry.AbstractInstanceRegistry {
...
protected volatile int numberOfRenewsPerMinThreshold; // 每分钟最小的续约数量
protected volatile int expectedNumberOfClientsSendingRenews; // 预期每分钟收到续约的客户端数量;等于当前eureka的服务数量
// 每分钟最小续约数量计算方法:
// 当前eureka的服务数量 * (60 / 配置的续约时间间隔,默认30s) * 心跳检测百分比,默认85%
// numberOfRenewsPerMinThreshold = 5 * (60/30) * 0.85 = 8.5 转换为整数位8 ,即每分钟最小的续约数量为8
protected void updateRenewsPerMinThreshold() {
this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
* (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
* serverConfig.getRenewalPercentThreshold());
}
}
计算每分钟收到的最小续约数量的公式:this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds()) * serverConfig.getRenewalPercentThreshold());
expectedNumberOfClientsSendingRenews
:
预期每分钟收到续约的客户端数量;等于当前eureka的服务数量,比如我现在启用了5台服务,这里就是5;
当服务上线、下线时,该参数会随之动态改变。
// 服务上线,预期收到续约的客户端数量+1
com.netflix.eureka.registry.AbstractInstanceRegistry#register
// The lease does not exist and hence it is a new registration
synchronized (lock) {
if (this.expectedNumberOfClientsSendingRenews > 0) {
// Since the client wants to register it, increase the number of clients sending renews
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
updateRenewsPerMinThreshold();
}
}
// 服务下线,预期收到续约的客户端数量-1
com.netflix.eureka.registry.AbstractInstanceRegistry#cancel
synchronized (lock) {
if (this.expectedNumberOfClientsSendingRenews > 0) {
// Since the client wants to cancel it, reduce the number of clients to send renews.
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
updateRenewsPerMinThreshold();
}
}
serverConfig.getExpectedClientRenewalIntervalSeconds()
: 服务的续约时间间隔,默认30s
serverConfig.getRenewalPercentThreshold()
: 检测自我保护机制阈值的百分比,默认15分钟内85%
numberOfRenewsPerMinThreshold
: 每分钟最小的续约数量;
所以可以得出每分钟最小的续约数量为: 5 * (60/30) * 85%,取整为8,和我们Eureka监控页面显示的值符合。
之后,如果我们停掉一台订单服务,等待1分钟后,此时Eureka还认为有5台服务,阈值要大于8台,而此时只有4台服务续约,30s续约一次,1分钟也就是8次,不满足每分钟最小的续约数量,所以Eureka会提示异常
自我保护机制的检测
定时任务,每60s判断最近1分钟的续约数量 是否满足 所要求的每分钟最小续约数量
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#isLeaseExpirationEnabled
@Override
public boolean isLeaseExpirationEnabled() {
if (!isSelfPreservationModeEnabled()) { // 如果没有开启自我保护机制,直接返回true
// The self preservation mode is disabled, hence allowing the instances to expire.
return true;
}
// 如果开启了自我保护机制,
// (刚才计算得出的 要求每分钟最小续约数量 >0) 且 (最近1分钟的续约数量 > 要求每分钟最小续约数量),则认为服务正常
return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
自我保护机制的优缺点
优点:在集群各节点没有出现宕机,而是由于网络抖动导致的续约失败,Eureka不会剔除该节点,不会造成误删
缺点:但是如果其中某一节点确实是宕机了,Eureka自我保护机制也不会将其剔除服务列表集合,当调用者负载均衡调用时就会出现访问不通,需要考虑重发机制
关闭Eureka的自我保护机制的配置:
# 关闭Eureka的自我保护机制,保证不可用的服务被及时剔除
eureka.server.enable-self-preservation=false
服务提供者下线 通知到 服务消费者
推送方式 :pull、push
服务是否持久化 :
高可用:集群特性,选举
CAP模型: C一致性 A可用性 P分区容错性
Eureka Netflix OSS 套件之一;
Eureka-server
Eureka-client