Eureka服务注册中心
SpringCloud组件,Eureka已停更。
内容简介
1、Eureka是什么
Eureka 是 Netflix 开发的,一个基于 REST 服务的,服务注册与发现的组件,以实现中间层服务器的负载平衡和故障转移。服务注册:将服务信息注册进注册中心;服务发现:从注册中心上获取服务信息。
它主要包括两个组件:Eureka Server 和 Eureka Client
- Eureka Client:一个Java客户端,用于简化与 Eureka Server 的交互(通常就是微服务中的客户端和服务端)
- Eureka Server:提供服务注册和发现的能力(通常就是微服务中的注册中心)
服务在Eureka上注册,然后每隔30秒发送心跳来更新它们的租约。如果客户端不能多次续订租约,那么它将在大约90秒内从服务器注册表中剔除。注册信息和更新被复制到集群中的所有eureka节点。来自任何区域的客户端都可以查找注册表信息(每30秒发生一次)来定位它们的服务(可能在任何区域)并进行远程调用
2、 Eureka 客户端与服务器之间的通信
服务发现有两种模式:一种是客户端发现模式,一种是服务端发现模式。Eureka采用的是客户端发现模式。
2.1. Register(注册)
Eureka客户端将关于运行实例的信息注册到Eureka服务器。注册发生在第一次心跳。
2.2. Renew(更新 / 续借)
Eureka客户端需要更新最新注册信息(续借),通过每30秒发送一次心跳。更新通知是为了告诉Eureka服务器实例仍然存活。如果服务器在90秒内没有看到更新,它会将实例从注册表中删除。建议不要更改更新间隔,因为服务器使用该信息来确定客户机与服务器之间的通信是否存在广泛传播的问题。
2.3. Fetch Registry(抓取注册信息)
Eureka客户端从服务器获取注册表信息并在本地缓存。之后,客户端使用这些信息来查找其他服务。通过在上一个获取周期和当前获取周期之间获取增量更新,这些信息会定期更新(每30秒更新一次)。获取的时候可能返回相同的实例。Eureka客户端自动处理重复信息。
2.4. Cancel(取消)
Eureka客户端在关机时向Eureka服务器发送一个取消请求。这将从服务器的实例注册表中删除实例,从而有效地将实例从流量中取出。
3、Eureka自我保护模式
如果 Eureka 服务器检测到超过预期数量的注册客户端以一种不优雅的方式终止了连接,并且同时正在等待被驱逐,那么它们将进入自我保护模式。这样做是为了确保灾难性网络事件不会擦除eureka注册表数据,并将其向下传播到所有客户端。
任何客户端,如果连续3次心跳更新失败,那么它将被视为非正常终止,病句将被剔除。当超过当前注册实例15%的客户端都处于这种状态,那么自我保护将被开启。
当自我保护开启以后,eureka服务器将停止剔除所有实例,保留服务注册信息,直到:它看到的心跳续借的数量回到了预期的阈值之上,或者自我保护被禁用。
默认情况下,自我保护是启用的,并且,默认的阈值是要大于当前注册数量的15%
4、Eureka VS Zookeeper
4.1. Eureka保证AP
Eureka服务器节点之间是对等的,只要有一个节点在,就可以正常提供服务。
Eureka客户端的所有操作可能需要一段时间才能在Eureka服务器中反映出来,随后在其他Eureka客户端中反映出来。也就是说,客户端获取到的注册信息可能不是最新的,它并不保证强一致性
4.2. Zookeeper保证CP
Zookeeper集群中有一个Leader,多个Follower。Leader负责写,Follower负责读,ZK客户端连接到任何一个节点都是一样的,写操作完成以后要同步给所有Follower以后才会返回。如果Leader挂了,那么重新选出新的Leader,在此期间服务不可用。
4.3. 为什么用Eureka
分布式系统大都可以归结为两个问题:数据一致性和防止单点故障。而作为注册中心的话,即使在一段时间内不一致,也不会有太大影响,所以在A和C之间选择A是比较适合该场景的。
单机
server
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
server:
port: 7001
spring:
application:
name: eureka-service
eureka:
instance:
# 单机eureka服务端的实例名称
hostname: localhost
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类添加@EnableEurekaServer
访问http://localhost:7001/
client
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
spring:
application:
name: xxx-service
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: xxx #主机名,7001页面,默认自己主机,一般是公司名,自定义
prefer-ip-address: true #访问路径显示ip地址,7001页面
启动类添加@EnableEurekaClient
server集群
高可用,防止单点故障
原理说明:
服务注册:将服务信息注册到注册中心
服务发现:从注册中心获取服务信息
实质:存key服务名,取value调用地址
步骤:
先启动eureka注册中心
启动服务提供者payment支付服务
支付服务启动后,会把自身信息注册到eureka
消费者order服务在需要调用接口时,使用服务别名去注册中心获取实际的远程调用地址
消费者获得调用地址后,底层实际是调用httpclient技术实现远程调用
消费者获得服务地址后会缓存在本地jvm中,默认每30秒更新异常服务调用地址
问题:微服务RPC远程调用最核心的是说明?
高可用,如果注册中心只有一个,出现故障就麻烦了。会导致整个服务环境不可用。
解决办法:搭建eureka注册中心集群,实现负载均衡+故障容错
互相注册,相互守望
window机集群搭建步骤(liunx机etc下也有hosts/不同服务器就不需要添加映射步骤)
添加映射
liunx机etc下也有hosts
修改C:\Windows\System32\drivers\etc下的hosts(本机映射地址)
末尾加上
# springcloud2020
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
这样做的效果是添加多个域名映射到本机
搭建eureka服务
如果是用服务器之间搭建集群,hostname可以直接localhost,端口可以统一7001,
defaultZone: http://ip2:7001/eureka/,http://ip3:7001/eureka/,其它类推
修改7001项目 applicaton.yml,可以不用服务名
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
修改7002项目 applicaton.yml,可以不用服务名
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
修改7003项目 applicaton.yml,可以不用服务名
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
集群效果
client修改配置
eureka:
client:
# 默认true,表示将自己注册进入server
register-with-eureka: true
# 默认true,集群必须设置true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
# 集群版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: xxx #主机名,7001页面,默认自己主机,一般是公司名,自定义
prefer-ip-address: true #访问路径显示ip地址,7001页面
client都注册到7001,7002,7003
client服务集群
提供服务的微服务也可以集群
准备好多个相同的微服务,提供服务的微服务,微服务的名字一致即可,同个机的话就需要改端口。
消费者微服务,调提供服务的微服务api接口,在RestTemplate中加入@LoadBalanced注解,启动类注入也行。@LoadBalanced//开启负载均衡,默认是轮询方法,平均访问。
/**
* @ClassName: ApplicationContextConfig
* @description: 配置类
* @author: WZQ
* @create: 2020/3/5 21:25
**/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced//开启负载均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
使用RestTemplate调用集群服务:
private final static String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";//集群微服务名字,都是cloud-payment-service
url = PAYMENT_URL + "/payment/create(地址)"
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}
只要所有的微服务都在server上注册信息,消费者微服务就可以通过eureka注册中心http://CLOUD-PAYMENT-SERVICE的微服务名字,轮询的访问提供服务的集群微服务(相同微服务名字绑定)。
服务发现
从注册中心上获取服务信息
查看在注册中心中的服务信息
/**
* 服务发现 获取服务信息
*/
@Resource
private DiscoveryClient discoveryClient;
/**
* 服务发现
*
* @return
*/
@GetMapping(value = "payment/discovery")
public Object discovery() {
//所有服务的名称
List<String> services = discoveryClient.getServices();
for (String element : services) {
log.info("*****element:" + element);
}
// 一个微服务下的全部实例,集群
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
log.debug(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + instance.getUri());
}
return this.discoveryClient;
}
启动类加入,@EnableDiscoveryClient
Eureka的自我保护机制
为什么会产生自我保护机制?
为防止EurekaClient可以正常运行,但是与EurekaServer网络不同的情况下,EurekaServer不会立刻将EurekaClient服务剔除。
什么是自我保护机制?
默认情况下,当Eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除(默认是90秒),但是,如果短时间内丢失大量的实例心跳,便会触发eureka server的自我保护机制。
比如在开发测试时,需要频繁地重启微服务实例,但是我们很少会把eureka server一起重启(因为在开发过程中不会修改eureka注册中心),当一分钟内收到的心跳数大量减少时,会触发该保护机制。可以在eureka管理界面看到Renews threshold和Renews(last min),当后者(最后一分钟收到的心跳数)小于前者(心跳阈值)的时候,触发保护机制,会出现红色的警告:
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认为虽然收不到实例的心跳,但它认为实例还是健康的,eureka会保护这些实例,不会把它们从注册表中删掉。
取消,页面也会显示TURNED OFF。
在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。
综上,自我保护模式是一种应对网络异常的安全保护措施它的架构哲学是宁可同时保留所有微服务,也不忙保姆注销如何健康的微服务,使用自我保护模式,可以让Eureka集群更加健壮,稳定。
署于CAP 的AP分支。
如何禁止自我保护机制
服务提供者:
eureka:
instance:
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳后等待时间上限 ,单位为秒(默认是90秒),超时剔除服务
lease-expiration-duration-in-seconds: 2
注册中心配置:
eureka:
server:
enable-self-preservation: false # 关闭自我保护机制 保证不可用服务及时清除
eviction-interval-timer-in-ms: 2000