Eureka是Netflix开源的一个服务注册发现工具,基于REST协议提供服务。也加入到Spring Cloud子项目中。本文介绍一下基于SpringBoot的Eureka快速使用,以及Eureka各个组件和相关配置的说明。
每一个Eureka服务都是一个实例(instance)。而一个实例在Eureka中有两种角色: Eureka Server 和 Eureka Client
当然,这些角色都不是绝对的,只是一个逻辑上的分类,一个实例可以具备Provider和Service的双重身份
这边我就不打算打造单节点了,直接整一个伪集群。 先开一个SpringBoot项目,pom.xml里引入以下依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> 复制代码
同时启动类上加上@EnableEurekaServer注解:
@SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } } 复制代码
再分别建立application-v1.properties,application-v2.properties,application-v3.properties 内容分别为:
spring.application.name=eureka-server server.port=7001 eureka.instance.hostname=peer1 eureka.client.service-url.defaultZone=http://localhost:7002/eureka/,http://localhost:7003/eureka/ 复制代码
spring.application.name=eureka-server server.port=7002 eureka.instance.hostname=peer1 eureka.client.service-url.defaultZone=http://localhost:7001/eureka/,http://localhost:7003/eureka/ 复制代码
spring.application.name=eureka-server server.port=7003 eureka.instance.hostname=peer1 eureka.client.service-url.defaultZone=http://localhost:7001/eureka/,http://localhost:7002/eureka/ 复制代码
可以看到,区别也仅仅是端口,不同。比如对于7001端口来说,它作为Eureka Server,同时也是7002和7003端口上Eureka Server的Client,从而搭建出一个Eureka集群
项目打成jar包后,分别执行
java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=v1 & java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=v2 & java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=v3 & 复制代码
启动项目,访问localhost:7001可以看到:
EUREKA-SERVER服务已经注册上去了,对应的节点有三个。那这样Eureka集群就已经搭建好了,当然这只是一个最简单的Demo
当Service Provider第一次向Eureka Server注册的时候,它会把自己的相关信息都发给Eureka Server,包括服务名,ip,端口等。Eureka Server接收到之后,将该信息写入到自己的缓存之中
Service Consumer会按照一定的频率从Eureka Server种拉去服务列表(如果是第一次拉取的话是全量获取,后续是增量获取),获取到服务列表后,用特定的方法(比如Ribbon用的就是轮询的方法来实现客户端的负载均衡)从服务列表种选取一个实例来发送请求
配置:
# 服务名,注册到Eureka Server上,客户端通过这个名字来获取服务列表 spring.application.name: eureka-service eureka.client.serviceUrl.defaultZone=http://localhost:7002/eureka/,http://localhost:7003/eureka/ # 是否向Eureka Server注册自己 eureka.client.register-with-eureka: true # 是否向Eureka Server获取注册信息 eureka.client.fetch-registry: true 复制代码
Eureka Server通过心跳检测来得知Provider的实时状态,心跳检测默认进行频率是30秒。当Eureka Server收到心跳包后,就进行一次续约。如果对应的服务状态发生了变化(比如机器下线了,或者新加入了一台机器),就要把这个更新写到一个“租约变更队列”之中,供后续Comsumer来获取
那Eureka Server怎么知道一个实例是否下线了呢?如果Eureka Server在90秒的时间内没有收到一个实例的心跳包,那么就会认为实例已经下线了,把这个记录更新到”租约变更队列“中
其实Client的下线可以有两种情况,一种是正常下线,在下线之前,Client会主动通知Eureka Server,使得Eureka Server能及时得知此消息;另一种是异常下线,比如进程崩溃,宕机,网络异常等情况,这种情况,Eureka Server90秒内没有收到心跳包就认为该实例下线
# 心跳频率,用来续约,单位:秒 eureka.instance.lease-renewal-interval-in-seconds: 30 # eureka server多久没有收到心跳,则表示对应的实例过期,单位:秒。 eureka.instance.lease-expiration-duration-in-seconds: : 90s 复制代码
Consumer和Eureka Server之间的通信主要是为了获取服务列表,以达到一致性。在Consumer第一次连接上Eureka Server的时候,会进行一次全量获取,后续会每隔30秒进行一次增量的获取,那增量获取的信息是从哪来的呢?还记得我们之前聊过的那个”租约变更队列“吗?实际上就是Eureka Server从队列中获取到的信息,然后进行同步
当然,在这个过程中,因为同步时间是30s,所以在30s中可能会出现信息不一致的情况。当然对于大多数的客户端来说,这个不一致带来的问题不会很大,因为重试功能是不可能没有的。但如果真的需要有更强的一致性的话,可以把这个时间缩短,但是同时也意味着Consumer的心态变得更加频繁了,由此可能会带来一些额外的网络开销,这是需要注意的。并且无论这个时间缩得多短,不一致性也是肯定会存在的。并且Eureka实现的是AP,而抛弃了C(强一致性),所以从这个方面来考虑,这个频率也没必要设置得太快
# 是否从Eureka-Server中获取服务实例注册信息,默认值为true eureka.client.fetch-registry: true # 从Eureka-Server拉取注册服务实例信息频率,默认:30 秒 eureka.client.registry-fetch-interval-seconds: 30 # 是否禁用增量获取服务实例注册信息,设置为true后,每次都是全量获取 eureka.disableDelta: false 复制代码
考虑这种情况:Eureka Server和Provider之间的网络通信出现了问题,但Provider还正常运行着,也还能对Consumer提供服务。但是由于它发送的心跳包无法到达Eureka Server,Eureka Server认为它下线了,于是从服务列表中移除了这个服务,这样Consumer获取到新的服务列表之后,就会误以为Provider服务不可用,进而导致一些问题
自我保护机制就是为了解决这个问题。当一分钟内Eureka Server收到的心跳包少于一个阈值的时候,就会开启自我保护机制,在这个状态中,Eureka Server不会从服务列表中移除实例(所以这个列表可能会有一些过期的实例,还是照样发给了Consumer)。当收到的心跳包数量达到阈值后,才会退出自我保护状态。
#是否开启自我保护模式。 eureka.server.enable-self-preservation: false #开启自我保护模式比例阈值,当收到心跳包占期望收到心跳包数量低于这个值,进入自我保护模式。 eureka.server.renewal-percent-threshold: 0.85 复制代码