Eureka解决了什么问题
这个问题本质是问服务注册中心的价值。很多年前,那个时候微服务还没有兴起,大家用Nginx+Tomcat就搞定了,加机器加Tomcat时候,直接在线改Nginx配置就搞定,这叫手动服务注册。但是在现今的微服务架构中,服务可能动不动就几十个,上百个,算上冗余就几百个,而且微服务的发布很频繁,不断有新的服务加入进来;在应对大流量场景时,还需要扩容缩容。这如果还依靠Nginx那种原始的手工配置方式,是不可行的,成本太高了。所以,就出现了服务注册发现,即服务实例自己会在启动后注册到服务注册中心,这叫服务注册,然后每个服务实例又从服务注册中心拉取服务注册表,动态的感知到服务的上下线,这叫服务发现。所以,服务注册中心这种技术是在微服务架构中自然而然就过渡过来的,并不突兀。eureka作为Netflix全家桶中的服务注册中心实现,其意义也就不言自明了。
快速体验Eureka
1、构建一个Eureka Server
创建一个maven工程,加入如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> </parent>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <spring.cloud-version>Hoxton.RELEASE</spring.cloud-version> </properties>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud-version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
|
创建一个application.yml文件,配置如下内容:
1 2 3 4 5 6 7
| server: port: 8761
eureka: client: register-with-eureka: false fetch-registry: false
|
#1 不要将此服务注册到eureka。因为当前服务已经是eureka server了
#2 不要拉取服务注册表。
创建一个启动类:
1 2 3 4 5 6 7 8 9 10
| @Slf4j @EnableEurekaServer @SpringBootApplication public class EurekaServer {
public static void main(String[] args) { SpringApplication.run(EurekaServer.class, args); log.info("eureka server 启动成功"); } }
|
启动后,见到如下输出即为成功:

2、开发一个服务提供者
1 2 3 4 5 6 7 8
| @RestController public class ServiceAController {
@GetMapping("/sayHello/{name}") public String sayHello(@PathVariable("name") String name) { return "hello," + name; } }
|
1 2 3 4 5 6 7 8 9
| @Slf4j @EnableEurekaClient @SpringBootApplication public class ServiceAApplication { public static void main(String[] args) { SpringApplication.run(ServiceAApplication.class, args); log.info("ServiceA 启动成功"); } }
|
创建一个application.yml文件,配置如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 8080
spring: application: name: serviceA
eureka: instance: hostname: loacalhost client: service-url: defaultZone: http://localhost:8761/eureka
|

3、开发一个服务调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @RestController @Configuration public class ServiceBController {
@Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); }
@Resource private RestTemplate restTemplate;
@GetMapping("/greeting/{name}") public String greeting(@PathVariable("name") String name) { return restTemplate.getForObject("http://serviceA/sayHello/" + name, String.class); } }
|
1 2 3 4 5 6 7 8 9 10
| @Slf4j @EnableEurekaClient @SpringBootApplication public class ServiceBApplication {
public static void main(String[] args) { SpringApplication.run(ServiceBApplication.class, args); log.info("ServiceB 启动成功"); } }
|
创建一个application.yml文件,配置如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 9000
spring: application: name: serviceB
# eureka client config eureka: instance: hostname: loacalhost client: service-url: defaultZone: http:
|
启动服务

4、进行实验
浏览器访问localhost:8761打开Eureka注册服务控制台,可发现ServiceA和B都注册到Eureka Server上了

在浏览器访问http://localhost:9000/greeting/jack,就可以看到结果了

5、画图讲解Hello Word原理
- 服务A和B的启动类上都有
@EnableEurekaClient注解,此注解会将服务变为Eureka客户端应用,将自己注册到Eureka Server上
- 服务B和服务A都会从Eureka Server拉取服务注册表
- 服务B发起调用服务A的请求时,Ribbon根据拉取到本地的服务注册表,将服务名替换为hostname+port并发起服务调用

Eureka集群实战
1、eureka注册中心集群
eureka如此重要的服务注册中心,肯定不可能是单点的。下面我们来构建一个eureka server集群。
分别使用如下配置各启动一个eureka server,组成一个集群。
1 2 3 4 5 6 7 8 9 10 11
| server: port: 8762
eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://peer2:8761/eureka instance: hostname: peer2
|
1 2 3 4 5 6 7 8 9 10 11
| server: port: 8761
eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://peer2:8762/eureka instance: hostname: peer1
|
小技巧:可以同一个工程,分别启动一次即可。
如果是IDEA的话,可能不让你同时启动两个,按照如下配置一下即可,在Allow parallel run前打上勾即可

随意访问一个eureka控制台,可发现两个节点已经组成集群。DS Replicas指的是当前eureka server的副本

小提示:假如访问peer2节点,会发现没有服务实例。不用着急,下面我们改造后就OK了。
2、将服务改造为注册到eureka server集群
分别将服务A和B的配置改为如下这样,变更部分为最后一行
1 2 3 4 5 6
| eureka: instance: hostname: localhost client: service-url: defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
|
改完重启后访问eureka控制台页面即可看到都有服务实例了。
服务健康自检机制
默认情况下,你的所有服务,比如服务A和服务B,都会自动给eureka注册中心同步心跳,续约。如果eureka server一段时间内没有感知到某个服务的心跳,就会把那个服务下线。
1、基于spring-boot-actuator的服务健康检查
服务加入如下依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
|
重启后访问http://localhost:port/actuator/health可以看到返回的服务状态,默认是UP
2、spring-cloud-eureka整合spring-boot-actuator实现自定义服务健康检查
1 2 3 4 5 6 7 8 9 10 11 12 13
|
@Component public class ServiceAHealthIndicator implements HealthIndicator {
@Override public Health health() { return new Health.Builder(Status.UP).build(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@Component public class ServiceAHealthCheckHandler implements HealthCheckHandler {
@Autowired private ServiceAHealthIndicator indicator;
@Override public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) { Status status = indicator.health().getStatus(); return InstanceInfo.InstanceStatus.UP; } }
|
eureka client会定时调用getStatus方法来判断服务实例的状态,如果状态变化了,就会通知eureka server。当服务实例挂掉了,那么eureka server就会感知到,然后下线这个服务实例。
3、生产经验分享
一般情况下很少使用这第二种方式自定义健康状态检查器,原因如下:
- 对于eureka
eureka默认是client通过心跳与eureka server保持心跳通信,如果心跳不及时或者没有心跳了,那么就说明那个服务挂了,然后eureka server就会摘除这个服务实例,这个机制足够用了
- 对于服务
在大规模部署中,每个服务都很复杂,不可能去一个个搞一堆健康检查的。大部分情况下,我们就是会对外暴露一个/health接口,然后专门外部定时调用各个服务的/health接口来判断当前这个服务状态就足够了。
Eureka常见配置
心跳检测
eureka客户端,默认每隔30秒发送一次心跳到eureka server(以后简称注册中心)。
1 2 3 4 5 6
| eureka: instance:
lease-renewal-interval-in-seconds:
lease-expiration-duration-in-seconds:
|
一般情况下,这俩参数建议不要修改
注册表抓取
默认情况下,客户端每隔30秒去注册中心抓取最新的注册表,然后缓存到本地。通过下面参数可修改时间间隔
1 2 3 4
| eureka: client: registry-fetch-interval-seconds:
|
自定义元数据
通过下面的metadata-map定义服务的元数据,反正就是你自己需要的一些东西,不过一般挺少使用的
1 2 3
| eureka: instance: metadata-map:
|
自我保护模式
如果在eureka控制台看到下面这样的东西:

这就是eureka进入了自我保护模式。如果客户端的心跳失败超过了一定的比例,或者说在一定时间内(默认15分钟)接收到的服务续约低于85%,那么就会认为是自己网络故障了,这个时候会导致人家client无法发送心跳,如果不加以控制,eureka服务实例摘除机制会将实例一个个的下掉。为了避免这种情况的发生,eureka这个时候会一种保护模式,保护服务注册表,不会立即把失效的服务实例摘除。在测试的时候一般会关闭这个自我保护模式:
1 2 3
| eureka: server: enable-self-preservation: false
|
总结
本篇博客,我们已经初步体会如何使用eureka,剖析了eureka的常见配置,后续我们马上进入源码剖析环节。