客户端健康检测与常用配置

  • Eureka常用配置
  • 客户端健康检测

1、回顾

  • 最小型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;
        }
    }

}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐