Spring Cloud_5_客户端健康检测与常用配置
客户端健康检测与常用配置Eureka常用配置客户端健康检测1、回顾回顾在微服务发布与调用中所讲的最简单Eureka集群架构图最小型Eureka集群Eureka客户端(服务者/调用者)分别向Eureka服务器进行注册通过浏览器访问调用者就可以间接访问到服务者Eureka客户端默认情况下每30秒会向Eureka服务器发送一个心跳检测请求,Eureka服务器则会知道...
·
客户端健康检测与常用配置
- Eureka常用配置
- 客户端健康检测
1、回顾
- 回顾在微服务发布与调用中所讲的最简单Eureka集群架构图
- 最小型Eureka集群
- Eureka客户端(服务者/调用者)分别向Eureka服务器进行注册
- 通过浏览器访问调用者就可以间接访问到服务者
- Eureka客户端默认情况下每30秒会向Eureka服务器发送一个心跳检测请求,Eureka服务器则会知道该Eureka客户端还存活,继续保持该客户端的服务列表
- 下面则介绍Eureka的常用配置
2、客户端常用配置
2.1、心跳配置
2.1.1、lease-renewal-interval-in-seconds
server:
port: 8080
spring:
application:
name: first-service-provider
eureka:
instance:
lease-renewal-interval-in-seconds: 5
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
logging:
level:
com.netflix: DEBUG
- 将默认每30秒一次的心跳请求修改为5S每次
- 为看到效果,我们在控制台输出日志
2.1.2、心跳测试
- 首先险需要启动服务器,即是先启动MyApplicationServer(atm_eureka_server)
- 启动MyApplicationProvider(atm_eureka_provider),查看控制台输入的日志
- 我们来查看输入的日志
2.1.3、lease-expiration-duration-in-seconds
- Eureka服务器在默认期限内(90S),接收不到Eureka客户端的心跳请求,说明该客户端可能出现故障
- 对于Eureka服务器而言,应该及时将该Eureka客户端清除
- 默认时间90s:服务器在90S内如果没有接收到客户端的心跳请求,则将该客户端从服务列表清除
- Eureka服务器内有一个定时器,默认每60S才开启,开启后才开始清除无效的客户端实例,换句话说,如果客户端在一定期限内没有发送心跳请求,Eureka服务器并不会立刻清除客户端实例,而是当定时器启动时才进行清除
2.1.4、心跳测试
- 修改Eureka服务器配置文件
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 5000
- 修改Eureka客户端配置文件
server:
port: 8080
spring:
application:
name: first-service-provider
eureka:
instance:
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 10
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
logging:
level:
com.netflix: DEBUG
- 开启Eureka服务器
- 开启Eureka客户端(服务提供者)
- 开启Eureka客户端成功后,成功向服务器注册,之后关闭Eureka客户端
- 规定时间内,客户端实例被清除
- 我们再看看关闭Eureka客户端之后,Eureka服务器的情况
2.2、服务列表抓取配置
- 客户端也会到服务器端抓取服务列表
- 编写一个服务调用者去服务器抓取服务列表
- 客户端每隔一定的时间就会向服务器抓取最新的服务列表,抓取成功后则保存在客户端本地
- 默认30S抓取一次
- 修改控制器
package com.atm.cloud;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.cloud.client.discovery.DiscoveryClient;
@RestController
@Configuration
public class InvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router() {
RestTemplate restTemplate = getRestTemplate();
// 根据应用名称调用服务
String json = restTemplate.getForObject(
"http://first-service-provider/person/1", String.class);
return json;
}
@Autowired
public DiscoveryClient discoveryClient;
//获取服务列表,并将服务列表的数量显示出来
//默认是30s抓取一次,可以根据需求进行修改
@GetMapping("/list")
@ResponseBody
public String serviceCount() {
List<String> serviceNames = discoveryClient.getServices();
for (String serviceId : serviceNames) {
List<ServiceInstance> serviceInstance = discoveryClient
.getInstances(serviceId);
System.out.println(serviceId + ": " + serviceInstance.size());
}
return "";
}
}
- 修改服务调用者的配置文件
server:
port: 9000
spring:
application:
name: first-service-invoker
eureka:
instance:
hostname: localhost
client:
registry-fetch-interval-seconds: 5
serviceUrl:
defaultZone: http://localhost:8761/eureka/
logging:
level:
com.netflix: DEBUG
- 启动服务调用者
2.3、元数据的配置和使用
2.3.1、元数据的配置
- 服务提供者
server:
port: 8080
spring:
application:
name: first-service-provider
eureka:
instance:
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 10
metadata-map:
company-name: aitemi
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
logging:
level:
com.netflix: DEBUG
2.3.2、元数据的使用
- 服务调用者控制类
package com.atm.cloud;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.cloud.client.discovery.DiscoveryClient;
@RestController
@Configuration
public class InvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/router", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String router() {
RestTemplate restTemplate = getRestTemplate();
// 根据应用名称调用服务
String json = restTemplate.getForObject(
"http://first-service-provider/person/1", String.class);
return json;
}
@Autowired
public DiscoveryClient discoveryClient;
//获取服务列表,并将服务列表的数量显示出来
//默认是30s抓取一次,可以根据需求进行修改
@GetMapping("/list")
@ResponseBody
public String serviceCount() {
List<String> serviceNames = discoveryClient.getServices();
for (String serviceId : serviceNames) {
List<ServiceInstance> serviceInstance = discoveryClient
.getInstances(serviceId);
System.out.println(serviceId + ": " + serviceInstance.size());
}
return "";
}
@GetMapping("/metedata")
@ResponseBody
public String getMetedata(){
System.out.println("Come into getMetedata...");
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("first-service-provider");
for (ServiceInstance serviceInstance : serviceInstances) {
String name=serviceInstance.getMetadata().get("company-name");
System.out.println(serviceInstance.getPort()+"---->>>"+name);
}
return "";
}
}
2.4、关闭自我保护模式
- 客户端会定时向服务器端发送心跳服务
- 如果失败率(默认15分钟85%)超过一定的比率,整个服务实例会被先保护起来,并不会立刻从服务列表中清除,从而可能引起其他客户端调用服务失败
- 自我保护模式可能导致灾难蔓延,为解决这种情况,一般会在其他客户端使用容错机制
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 5000
3、健康检测监控
- 在实际开发中,还有可能出现一种情况
- 客户端的服务需要连接数据库,可是数据库已经崩溃,但是客户端仍然向服务器端发送心跳请求,这种情况下服务器端误认为客户端的服务是正常的
- 客户端应该及时告诉服务器端自己的健康状态
- 加入Actuator,提供了很多端点,其中一个叫做/heath端点
3.1、健康监控
3.1.1、服务提供者引入依赖
<!-- 健康检测依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
3.1.2、配置文件
server:
port: 8080
spring:
application:
name: first-service-provider
endpoints:
sensitive: false
eureka:
instance:
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 10
metadata-map:
company-name: aitemi
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
logging:
level:
com.netflix: DEBUG
3.1.3、端口测试
3.2、健康模拟
3.2.1、服务提供者控制器
package com.atm.cloud;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
//能否访问数据库
public static boolean canVisitOb=true;
@RequestMapping(value = "/db/{can}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public void setOb(@PathVariable boolean can){
this.canVisitOb=can;
}
}
3.2.2、服务提供者健康监控器
- Eureka客户端(服务提供者)实现健康监控器
package com.atm.cloud;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
/**
* 健康指示器
* @author Aitemi
*
*/
@Component
public class MyHealthIndicator implements HealthIndicator{
public Health health() {
//如果canVisitOb是true,返回健康状态:UP,否则返回健康状态:DOWN
if(MyController.canVisitOb){
return new Health.Builder(Status.UP).build();
}else{
return new Health.Builder(Status.DOWN).build();
}
}
}
- 我们可以发现,虽然客户端已经修改了健康状态,但是服务器端并不知道,所以还需要编写一个健康处理器
3.2.3、服务提供者健康处理器
package com.atm.cloud;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
/**
* 健康检测处理器
* @author Aitemi
*
*/
@Component
public class MyHealthCheckHandler implements HealthCheckHandler{
@Autowired
private MyHealthIndicator myHealthIndicator;
//默认状态,每30S执行一次该方法,本人已经修改为10S
public InstanceStatus getStatus(InstanceStatus currentStatus) {
//先获取客户端的服务状态
Status status=myHealthIndicator.health().getStatus();
//将状态传播至服务器
if(status.equals(Status.UP)){
return InstanceStatus.UP;
}else{
return InstanceStatus.DOWN;
}
}
}
更多推荐
已为社区贡献8条内容
所有评论(0)