Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理。在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
如何使用
在springboot项目中使用Eureka十分简单,只需要以下几步就可以启动一个server了。
pom文件中添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
修改yml文件
server:
port: 8610
spring:
application:
name: datax-eureka
eureka:
instance:
lease-renewal-interval-in-seconds: 20
lease-expiration-duration-in-seconds: 60
# 设置使用IP
prefer-ip-address: true
# 设置外网IP号
ip-address: localhost
client:
register-with-eureka: false
fetch-registry: false
instance-info-replication-interval-seconds: 40
service-url:
defaultZone: http://${eureka.instance.ip-address}:${server.port}/eureka/
启动类添加@EnableEurekaServer注解
@EnableEurekaServer
@SpringBootApplication
public class DemoEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(DataxEurekaApplication.class, args);
}
}
浏览器访问http://127.0.0.1:8610/可以看到一个简单的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 BEING EXPIRED JUST TO BE SAFE.
出现这个错误的原因是因为Eureka进入自我保护模式
自我保护模式
默认情况下,如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障(比如网络故障或频繁的启动关闭客户端),Eureka Server自动进入自我保护模式。不再剔除任何服务,当网络故障恢复后,该节点自动退出自我保护模式。
可以使用eureka.server.enable-self-preservation=false来禁用自我保护模式,生产环境不建议这么做。
还有一种方式是把阈值因子eureka.server.renewalPercentThreshold调低,默认是0.85,如果阈值比最小值大,则自我保护模式开启
eureka:
server:
enable-self-preservation: true
#eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒
eviction-interval-timer-in-ms: 60000
#阈值更新的时间间隔,单位为毫秒,默认为15 * 60 * 1000
renewal-threshold-update-interval-ms: 15 * 60 * 1000
#阈值因子,默认是0.85,如果阈值比最小值大,则自我保护模式开启
renewal-percent-threshold: 0.85
#清理任务程序被唤醒的时间间隔,清理过期的增量信息,单位为毫秒,默认为30 * 1000
delta-retention-timer-interval-in-ms: 30000
客户端健康检查
默认情况下,Eureka使用客户端心跳来确定客户端是否已启动。在成功注册后,Eureka始终宣布应用程序处于“UP”状态,这就引发一个问题,客户端虽然是启动的,但是可能由于某种原因(比如数据库宕机)无法提供正确的服务。为了解决这个问题,需要引入健康检查机制。
前置条件:启动一个server(本文端口为8610),下面开始创建client(端口为8611)
Eureka的健康检查依赖于Springboot-actuator的/health,需要在pom.xml中引入spring-boot-starter-actuator模块的依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
在eureka客户端中的application.properties或yml文件中配置:eureka.client.healthcheck.enabled=true,就可以改变eureka server对客户端健康检测的方式,改用actuator的/actuator/health端点来检测。
# 注册中心配置
eureka:
instance:
lease-renewal-interval-in-seconds: 20
# 设置使用IP
prefer-ip-address: true
# 设置外网IP号
ip-address: localhost
client:
healthcheck:
enabled: true
register-with-eureka: true
fetch-registry: true
instance-info-replication-interval-seconds: 30
registry-fetch-interval-seconds: 3
service-url:
defaultZone: http://localhost:8610/eureka
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
新建HealthIndicatorImpl 并实现HealthIndicator,自定义监控的逻辑。
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class HealthIndicatorImpl implements HealthIndicator {
private boolean up = true;
@Override
public Health health() {
if (up) {
return new Health.Builder().withDetail("status", "up").up().build(); //自定义监控内容
} else {
return new Health.Builder().withDetail("error", "client is down").down().build();
}
}
public boolean isUp() {
return up;
}
public void setUp(boolean up) {
this.up = up;
}
}
在启动类上添加注解@EnableDiscoveryClient 或@EnableEurekaClient(@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心)
客户端启动后,sever端显示客户端服务状态为UP
访问http://127.0.0.1:8611/actuator/health 查看客户端状态
{
"status": "UP",
"components": {
"configServer": {
"status": "UP",
"details": {
"repositories": [
{
"name": "app",
"profiles": [
"default"
],
"label": null
}
]
}
},
"discoveryComposite": {
"status": "UP",
"components": {
"discoveryClient": {
"status": "UP",
"details": {
"services": [
"datax-config"
]
}
},
"eureka": {
"description": "Remote status from Eureka server",
"status": "UP",
"details": {
"applications": {
"DATAX-CONFIG": 1
}
}
}
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 107373129728,
"free": 6842404864,
"threshold": 10485760,
"exists": true
}
},
"healthIndicatorImpl": {
"status": "UP",
"details": {
"status": "up"
}
},
"hystrix": {
"status": "UP"
},
"ping": {
"status": "UP"
},
"refreshScope": {
"status": "UP"
}
}
}
为了方便模拟,创建个controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HealthController {
@Autowired
private HealthIndicatorImpl myHealthChecker;
@RequestMapping("/up")
public String up(@RequestParam("up") Boolean up) {
myHealthChecker.setUp(up);
return myHealthChecker.isUp() ? "UP" : "DOWN";
}
}
访问http://127.0.0.1:8611/up?up=false更改客户端状态, server端可以看到状态由UP变为DOWN了。
Eureka 工作流程
- Eureka Server 启动成功,等待服务端注册
- Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务
- Eureka Client 会每 30 秒向 Eureka Server 发送一次心跳,证明客户端服务正常
- 当 Eureka Server90 秒内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例
- 单位时间内 Eureka Server 统计到大量的 Eureka Client 没有发送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有发送心跳的客户端
- 当 Eureka Client 心跳恢复正常后,Eureka Server 自动退出自我保护机制
- Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存在本地
- 服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存
- Eureka Client 获取到目标服务器信息,发起服务调用
10.Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中剔除