SpringCloud 01 —— 注册中心之Eureka

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。

Eureka由两个组件组成:Eureka Server和Eureka Client。

Eureka Serve

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。


Eureka Client

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

Eureka 不再维护

2018年7月,官方突然宣布Eureka 2.x停止开源计划并不在进行维护

但是Eureka本身性能很高也非常稳定,即使不维护也不影响你继续使用Spring Cloud,Eureka本身服务注册/发现这些功能已经够用。所以现在很多公司依然在使用Eureka。当然如果Eureka不能满足你的业务需求也可以使用SpringCloud官方推荐的Consul或者Zookeeper进行替代(这里我们先写出一个可用的微服务整体,后面章节再进行其他组件的替换讲解)。

Eureka服务端搭建

创建一个SpringBoot项目,并导入EurekaServer依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

然后我们只需要在启动类上加上@EnableEurekaServer注解即可:

@SpringBootApplication
@EnableEurekaServer
public class SpringcloudApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudApplication.class, args);
    }
}

接着配置服务器参数application.yml:

server:
  port: 8761
spring:
  application:
    name: cloud-register-eureka

此时我们启动程序,通过浏览器访127.0.0.1:8761
进入如下界面:

但是控制台有抛出异常:

com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
	at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187) ~[jersey-apache-client4-1.19.1.jar:1.19.1]
	at com.sun.jersey.api.client.filter.GZIPContentEncodingFilter.handle(GZIPContentEncodingFilter.java:123) ~[jersey-client-1.19.1.jar:1.19.1]
	at com.netflix.discovery.EurekaIdentityHeaderFilter.handle(EurekaIdentityHeaderFilter.java:27) ~[eureka-client-1.9.17.jar:1.9.17]
	at com.sun.jersey.api.client.Client.handle(Client.java:652) ~[jersey-client-1.19.1.jar:1.19.1]
...

这里的Connection refused: connect主要是因为服务器在启动时,会把自己当做一个客户端去注册Eureka服务器,并且会到Eureka服务器中抓取注册信息,它自己本身只是一个服务器而不是服务器的提供者,因此可以修改application.yml文件,取消注册。添加如下配置:

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

重新启动异常消失。

Eureka服务端集群搭建

上面我讲了Eureka服务器的创建,这里我们讲说明如何在不改变代码的情况,仅仅调整配置就可以实现集群部署。

这里我们模拟两个Eureka服务组成的集群,实际可以根据需要发布多个,原理都是一样的,集群间的Eureka会相互注册,对外提供服务,并且同步Eureka服务上已经同步的注册服务信息。

修改服务端的配置文件application.yml

server:
  port: 8761 #端口号
spring:
  application:
    name: cloud-register-eureka-01 #服务器名
eureka:
  client:
    #注释掉,启用注册。即服务A和服务B互相注册
    #register-with-eureka: false
    #fetch-registry: false
    #添加注册信息,多个用逗号(,)分割
    service-url:
      defaultZone: http://127.0.0.1:8762/eureka

启动程序,这个时候会报前面相同的异常,不用管因为另一个服务还没有启动所以没连上。然后继续修改配置(注意是同项目,主要是为了方便演示不新开项目模块)application.yml:

server:
  port: 8762 #端口号
spring:
  application:
    name: cloud-register-eureka-02 #服务器名
eureka:
  client:
    #注释掉,启用注册。即服务A和服务B互相注册
    #register-with-eureka: false
    #fetch-registry: false
    #添加注册信息,多个用逗号(,)分割
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka

修改idea的启动方式为并行运行(可以同时运行一个项目)

再次启动项目(原来的不要关).

这是我们分别访问127.0.0.1:8761127.0.0.1:8762发现彼此注册成功。

这样一个简单的EurekaServer集群就构建完成了。

Server 端重要参数(加粗为常用)

参数描述默认
eureka.server.eviction-interval-timer-in-ms清理无效节点的时间间隔默认60秒
eureka.dashboard.enabled是否开启仪表盘默认true
eureka.dashboard.path仪表盘访问路径默认 /
eureka.server.enable-self-preservation是否开启自我保护默认true
fetch-registryserver 获取注册表默认true
eureka.instance.registry.default-open-for-traffic-count注册服务中心,默认打开的通信数量默认 1
eureka.instance.registry.expected-number-of-clients-sending-renews指定每分钟需要收到的续约次数值默认 1
eureka.server.a-s-g-cache-expiry-timeout-ms缓存ASG信息的到期时间,单位为毫秒默认 600000L
eureka.server.a-s-g-query-timeout-ms查询AWS上ASG(自动缩放组)信息的超时值,单位为毫秒300
eureka.server.a-s-g-update-interval-ms从AWS上更新ASG信息的时间间隔,单位为毫秒300000L
eureka.server.a-w-s-access-id获取aws访问的id,主要用于弹性ip绑定,此配置是用于aws上的-
eureka.server.a-w-s-secret-key获取aws私有秘钥,主要用于弹性ip绑定,此配置是用于aws上的-
eureka.server.batch-replication表示集群节点之间的复制是否为了网络效率而进行批处理false
eureka.server.binding-strategy获取配置绑定EIP或Route53的策略-
eureka.server.delta-retention-timer-interval-in-ms清理任务程序被唤醒的时间间隔,清理过期的增量信息,单位为毫秒30000L
eureka.server.disable-delta增量信息是否可以提供给客户端看false
eureka.server.disable-delta-for-remote-regions增量信息是否可以提供给客户端或一些远程地区false
eureka.server.disable-transparent-fallback-to-other-region如果在远程区域本地没有实例运行,对于应用程序回退的旧行为是否被禁用false
eureka.server.e-i-p-bind-rebind-retries获取服务器尝试绑定到候选的EIP的次数3
eureka.server.e-i-p-binding-retry-interval-ms服务器检查ip绑定的时间间隔,仅仅是稳定状态检查,单位为毫秒0
eureka.server.e-i-p-binding-retry-interval-ms-when-unbound服务器检查ip绑定的时间间隔,单位为毫秒0
eureka.server.enable-replicated-request-compression复制的数据在发送请求时是否被压缩FALSE
eureka.server.eviction-interval-timer-in-ms过期实例应该启动并运行的时间间隔,单位为毫秒60000L
eureka.server.g-zip-content-from-remote-regioneureka服务器中获取的内容是否在远程地区被压缩true
eureka.server.json-codec-name如果没有设置默认的编解码器将使用全JSON编解码器,获取的是编码器的类名称-
eureka.server.list-auto-scaling-groups-role-name用来描述从AWS第三账户的自动缩放组中的角色名称ListAutoScalingGroups
eureka.server.log-identity-headersEureka服务器是否应该登录clientAuthHeaderstrue
eureka.server.max-elements-in-peer-replication-pool复制池备份复制事件的最大数量10000
eureka.server.max-elements-in-status-replication-pool可允许的状态复制池备份复制事件的最大数量10000
eureka.server.max-idle-thread-age-in-minutes-for-peer-replication复制线程可以保持存活的空闲时间,默认分钟15
eureka.server.max-idle-thread-in-minutes-age-for-status-replication状态复制线程可以保持存活的空闲时间,单位分钟10
eureka.server.max-threads-for-peer-replication获取将被用于复制线程的最大数目20
eureka.server.max-threads-for-status-replication被用于状态复制的线程的最大数目1
eureka.server.max-time-for-replication尝试在丢弃复制事件之前进行复制的时间,单位毫秒30000
eureka.server.min-available-instances-for-peer-replication正常的对等服务instance最小数量。-1表示服务中心为单节点-1
eureka.server.min-threads-for-peer-replication获取将被用于复制线程的最小数目5
eureka.server.min-threads-for-status-replication被用于状态复制的线程的最小数目1
eureka.server.number-of-replication-retries获取集群里服务器尝试复制数据的次数5
eureka.server.peer-eureka-nodes-update-interval-ms集群里eureka节点的变化信息更新的时间间隔,单位为毫秒600000
eureka.server.peer-eureka-status-refresh-time-interval-ms服务器节点的状态信息被更新的时间间隔,单位为毫秒30000
eureka.server.peer-node-connect-timeout-ms连接对等节点服务器复制的超时的时间,单位毫秒200
eureka.server.peer-node-connection-idle-timeout-secondshttp连接被清理之后服务器的空闲时间,单位秒30
eureka.server.peer-node-read-timeout-ms读取对等节点服务器复制的超时的时间,单位为毫秒200
eureka.server.peer-node-total-connections获取对等节点上http连接的总数1000
eureka.server.peer-node-total-connections-per-host获取特定的对等节点上http连接的总数500
eureka.server.prime-aws-replica-connections对集群中服务器节点的连接是否应该准备true
eureka.server.property-resolver配置分解器-
eureka.server.rate-limiter-burst-size速率限制的burst size ,这里用的是令牌桶算法10
eureka.server.rate-limiter-enabled限流是否应启用或禁用false
eureka.server.rate-limiter-full-fetch-average-rate速率限制器用的是令牌桶算法,此配置指定平均执行请求速率100
eureka.server.rate-limiter-privileged-clients认证的客户端列表,这里是除了标准的eureka Java客户端。-
eureka.server.rate-limiter-registry-fetch-average-rate速率限制器用的是令牌桶算法,此配置指定平均执行注册请求速率500
eureka.server.rate-limiter-throttle-standard-clients是否对标准客户端进行限流false
eureka.server.registry-sync-retries当eureka服务器启动时尝试去获取集群里其他服务器上的注册信息的次数5
eureka.server.registry-sync-retry-wait-ms当eureka服务器启动时获取其他服务器的注册信息失败时,会再次尝试获取,期间需要等待的时间30000
eureka.server.remote-region-app-whitelist必须通过远程区域中检索的应用程序的列表-
eureka.server.remote-region-connect-timeout-ms连接到对等远程地eureka节点的超时时间,单位毫秒1000
eureka.server.remote-region-connection-idle-timeout-seconds http连接被清理之后远程地区服务器的空闲时间,单位秒
eureka.server.remote-region-fetch-thread-pool-size用于执行远程区域注册表请求的线程池的大小20
eureka.server.remote-region-read-timeout-ms获取从远程地区eureka节点读取信息的超时时间,单位毫秒1000
eureka.server.remote-region-registry-fetch-interval从远程区域取出该注册表的信息的时间间隔,单位秒30
eureka.server.remote-region-total-connections获取远程地区对等节点上http连接的总数1000

Eureka客户端搭建

新建一个client-eureka的服务作为服务端,在pom文件中加入Spring Cloud 的依赖,如下代码清单

    <parent>
        <artifactId>cloud-client</artifactId>
        <groupId>group.zhouning</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-client</artifactId>
    <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-web</artifactId>
        </dependency>
    </dependencies>

在application.yml中添加配置

server:
  port: 8762
spring:
  application:
    name: register-eureka-client
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka

配置中将应用名称设置为register-eureka-client,该服务会注册到上节创建的Eureka Server上面去,注意端口及地址是否正确。现在我们重新写一个RESTFUL 接口。

启动类
和 server一样我们只需要在启动类前加上@EnableDiscoveryClient注解即可

@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class, args);
    }
}

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    /**
     * 主键Id
     */
    private long id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 昵称
     */
    private String rolename;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 备注
     */
    private String remark;
}

其中 @Data @AllArgsConstructor @NoArgsConstructor 都是lombok提供注解需要引入lombok依赖,idea可以直接在插件里面搜索lombok安装,eclipse(包括STS)需要自行到官网下载对应的插件安装(不过这些应该都会吧)

服务类

public interface UserService {

    /**
     * 模拟数据库获取所有用户
     * @return
     */
    List<User> getUsers();

    /**
     * 模拟数据库根据id获取用户
     * @return
     */
    User getUserById(long id);
}
@Service
public class UserServiceImpl implements UserService {

    @Override
    public List<User> getUsers() {
        return initUser();
    }

    @Override
    public User getUserById(long id) {
        List<User> userList = getUsers().stream().filter(user -> user.getId() == id).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(userList)) {
            return new User(0, null, null, null, "用户不存在!");
        }
        return userList.get(0);
    }

    /**
     * 模拟数据库初始化数据
     *
     * @return
     */
    private List<User> initUser() {

        List<User> userList = new ArrayList<>();
        User user1 = new User(1, "007", "詹姆斯邦德", "zms@gmail.com", "12345");
        User user2 = new User(2, "9527", "华安", "h_a@gmail.com", "321");
        User user3 = new User(3, "47", "代号47", "47@gmail.com", "请扫描头顶二维码");

        userList.add(user1);
        userList.add(user2);
        userList.add(user3);

        return userList;
    }
}

控制类

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 根据id获取用户
     * @param id 用户id
     * @return
     */
    @GetMapping("/{id:\\d+}")
    public User getUserById(@PathVariable Long id){
        return userService.getUserById(id);
    }

    /**
     * 获取全部用户
     * @return
     */
    @GetMapping
    public List<User> getUsers(){
        return userService.getUsers();
    }


    @GetMapping("/hi")
    public String hello() {
        return "hello";
    }
}

启动我们开篇写的eureka(注意这里不是集群,当然集群也可以),eureka-server配置如下:

server:
  port: 8761
spring:
  application:
    name: register-eureka-01
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
#    service-url:
#      defaultZone: http://127.0.0.1:8762/eureka

然后启动刚刚写好的客户端程序。访问127.0.0.1:8761 .

发现客户端注册成功。但是这是可能会出现如下提示:
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自我保护,可以通过eureka.server.enable-self-preservation为false 进行关闭

Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告

Eurake有一个配置参数eureka.server.renewalPercentThreshold,定义了renews 和renews threshold的比值,默认值为0.85。当server在15分钟内,比值低于percent,即少了15%的微服务心跳,server会进入自我保护状态,Self-Preservation。在此状态下,server不会删除注册信息,这就有可能导致在调用微服务时,实际上服务并不存在。
这种保护状态实际上是考虑了client和server之间的心跳是因为网络问题,而非服务本身问题,不能简单的删除注册信息

解决办法除了上述关闭知我保护外,还可以生产上可以开自注册,部署多个server。

然后我们就可以进行简单的测试了,使用Chrome的ApiDebug插件(或者postman等调试工具)访问对应地址,如

集群模式

客户端连接集群也非常简单,只需要在service-url配置中用逗号分割填入多个地址即可.

如:

server:
  port: 8763 #如有冲突,修改一下端口
spring:
  application:
    name: register-eureka-client
eureka:
  client:
    service-url:
        defaultZone: http://127.0.0.1:8761/eureka,http://127.0.0.1:8762/eureka

然后按前面集群的方式启动多个server,再启动客户端验证即可。

Eureka密码访问

为了保护Eureka 的服务器的安全性,不行被其它微服务注册,我们可以对Eureka 设置一层密码,使只有知道Eureka 账号密码的才可以连接(当然如果是公司内网的话,设不设置密码看情况)

  1. 修改eureka server注册服务的maven,添加以下依赖,至于Security 的其他使用功能,后续章节会讲到。
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
  1. 修改应用配置

    固定用户名和密码,否则每次密码都会是随机的,其它服务也注册不上来。

server:
  port: 8761
spring:
  application:
    name: register-eureka-01
  security:  #开启密码功能需要
    user:
      name: xunfei
      password: 12345
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
#    service-url:
#      defaultZone: http://127.0.0.1:8762/eureka
  1. 添加Security配置类

    主要是因为高版本丢弃了security. basic.enabled= true配置,并且需要注意需要设置httpBasic(),否则不支持http://${user}:${password}@${host}:${port}/eureka/ 这种方式登录

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .and().authorizeRequests().anyRequest().authenticated()
                .and().csrf().disable();
    }
}

这时我们启动eureka server,访问之前的127.0.0.1:8761会弹出提示框要求我们输入密码。这时输入刚刚配置的密码即可。

客户端注册

客户端只需要像使用mysql中的用户名密码那样在前面的访问地址前加上【用户名:密码】 + @即可。

server:
  port: 8762
spring:
  application:
    name: register-eureka-client
eureka:
  client:
    service-url:
      #http://用户名:密码@地址 密码访问
      defaultZone: http://xunfei:12345@127.0.0.1:8761/eureka

启动客户端,会发现成功注册

Eureka 客户端配置

常用配置:

参数描述默认
eureka.client.service-url指定服务注册中心地址,类型为 HashMap,并设置有一组默认值,如果服务注册中心为高可用集群时,多个注册中心地址以逗号分隔。如果服务注册中心加入了安全验证,这里配置的地址格式为: http://:@localhost:8761/eureka默认的Key为 defaultZone;默认的Value为 http://localhost:8761/eureka
eureka.client.fetch-registry是否从 Eureka Server 获取注册信息默认true
eureka.client.register-with-eureka是否要将自身的实例信息注册到 Eureka Server默认true
eureka.client.eureka-connection-idle-timeout-secondsEureka Server连接空闲关闭时间,单位:秒默认30
eureka.client.eureka-server-connect-timeout-seconds连接 Eureka Server 的超时时间,单位:秒默认5
eureka.client.eureka-server-read-timeout-seconds读取 Eureka Server 信息的超时时间,单位:秒默认8
eureka.client.eureka-server-total-connections从 Eureka Client 到所有Eureka Server的连接总数默认200
eureka.client.eureka-server-total-connections-per-host从 Eureka Client 到每个 Eureka Server 主机的连接总数默认50
eureka.client.eureka-service-url-poll-interval-seconds轮询 Eureka Server 地址更改的间隔时间,单位:秒。默认0
eureka.client.initial-instance-info-replication-interval-seconds初始化实例信息到 Eureka Server 的间隔时间,单位:秒默认40
eureka.client.instance-info-replication-interval-seconds更新实例信息的变化到 Eureka Server 的间隔时间,单位:秒默认30
eureka.client.registry-fetch-interval-seconds从 Eureka Server 获取注册信息的间隔时间,单位:秒默认30

其他的参数自行了解,见org\springframework\cloud\spring-cloud-netflix-eureka-server\2.2.0.RELEASE\spring-cloud-netflix-eureka-server-2.2.0.RELEASE.jar!\META-INF\spring-configuration-metadata.json中。

完整的Eureka配置百多项,当然绝大部分是不需要关注的,常用的就好。

Eureka 服务健康检查

在默认情况下,Eureka的客户端默认每隔30秒会发送一次心跳给服务器端,告知服务端自己是否还存活,但是实际环境中可能出现客户端表面上可以正常发送心跳,但实际上服务是不可用的。

场景一
比如一个和第三方系统对接的服务,第三方系统可能已经挂了或者早已失效,客户端应当告诉服务端当前第三方系统的状态,实现该功能,可以使用Eureka的健康检查控制器。

场景二
我们需要在对外提供的服务升级的时候,如果新发的服务发送成功,让老的系统处理完业务慢慢下线,所有调用此服务的应用全部走新发的引用。

其它
其它需要对注册上Eureka 服务器上的实例做手动下线和上线的操作

Sring Boot Actuator的使用

Spring Boot Actuator 模块主要用于系统监控,系统整合Spring Boot Actuator 后,它会对外提供多个服务店,可以让外部系统看到应用程序的健康情况,比如/actuator/health端点。

新建模块cloud-actuator,并在maven中引入相关依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

启动类

@EnableDiscoveryClient
@SpringBootApplication
public class ActuatorApplication {
    public static void main(String[] args) {
        SpringApplication.run(ActuatorApplication.class, args);
    }
}

添加项目配置

server:
  port: 8863
spring:
  application:
    name: cloud-actuator
eureka:
  client:
    service-url:
      defaultZone: http://xunfei:12345@127.0.0.1:8761/eureka

启动cloud-actuator项目访问127.0.0.1:8863/actuator/health

表示当前服务的状态是可用状态

*除/heath外,详细的endpoints:actuator endpoints列表

同时可以通过

management:
  endpoint:
    shutdown:
      enabled=true:

选择启用还是关闭(默认开启)

实现应用程序自检

我们需要做两件事,之后将结果告诉服务端

  1. 让客户端自己进行检查,是否能连接其他服务
  2. 将连接第三方服务的结果和客户端的状态关联

我们使用Spring Boot Cloud 可以直接实现一个自定义的健康检查器。根据能否访问第三方应用,来决定应自己的健康状态。

实现一个自定义的健康检查器

实现一个自定义的检查器需要实现HealthIndicator接口,HealthIndicator接口中包含一个默认的实现和一个未实现的接口(需要实现),默认实现的getHealth(boolean includeDetails)可以根据需要自定义实现 ,getHealth所调用的this.health()使我们即将要实现的。

@FunctionalInterface
public interface HealthIndicator extends HealthContributor {
    default Health getHealth(boolean includeDetails) {
        Health health = this.health();
        return includeDetails ? health : Health.status(health.getStatus()).build();
    }
    Health health();
}
@Component
public class CustomHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        if(HealthStatus.isVisited){
            //成功和第三方连接
            return new Health.Builder(Status.UP).build();
        }else {
            //和第三方连接失败
            return new Health.Builder(Status.DOWN).build();
        }
    }
}

HealthStatus是一个全局共享的变量,这里是模拟是否可以访问第三方应用,boolean 的默认值是false

@Data
public class HealthStatus {
    /**
     * 状态是否可访问
     */
    public static boolean isVisited;
}

为了方便测试功能和演示,我们用一个接口还模拟与第三方应用的请求是否通畅。

@RestController
@Slf4j
public class CustomHealthController {
    @PostMapping("/healthStatus")
    public ResponseEntity<String> healthStatus(boolean status) {
        HealthStatus.isVisited = status;
        log.info("当前与第三方连接是否可用,{}", status);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

我们通过127.0.0.1:8863/actuator/health访问,此时指示器会根据HealthStatus.isVisited判断健康值,如果是true,返回的就是“UP”状态,反之返回“DOWN”,因为HealthStatus的默认值是false,所以会返回“DOWN”

{
"status": "DOWN"
}

使用postman,给127.0.0.1/healthStatus发送请求,参数status设为true。再次通过127.0.0.1:8863/actuator/health访问得到up

{
"status": "UP"
}

健康检查处理器

接下来,如果服务提供者想把健康状态通知给服务器,还需要实现“健康检查处理器(HealthCheckHandler)”,健康检查处理器先将健康状态保存在内存中,一旦状态放生改变,就会重新想Eureka服务器进行注册,如果是将“DOWN”的健康状态同步给Eureka服务器,其他应用将拿不到这写不可用实例。

@Component
@Slf4j
public class CustomHealthCheckHandler implements HealthCheckHandler {

    @Autowired
    private CustomHealthIndicator myHealthIndicator;

    @Override
    public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus instanceStatus) {
        if(myHealthIndicator.health().getStatus().equals(Status.UP)){
            log.info("第三方服务可以正常访问");
            return InstanceInfo.InstanceStatus.UP;
        }else{
            log.info("第三方服务访问异常");
            return InstanceInfo.InstanceStatus.DOWN;
        }
    }
}

自定义检查器中需要注入自定义的指示器,根据自定义指示器的结果返回不同的状态,实际上Eureka内部会启动一个定时器,定时更新本地的实例信息,并且执行处理器中的getStatus方法,再将服务器中的状态更新到Eureka服务器中。默认30秒执行一次,可以通过修改eureka.client.instance-info-replication-interval-seconds 配置来改变定时时间,比如设置为10秒.

然后我们可以看到控制台信息,通过上面的方法再次进行测试。

更新时间:2020-04-11 17:54:52

本文由 寻非 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://www.zhouning.group/archives/springcloud01注册中心之eureka
最后更新:2020-04-11 17:54:52

评论

Your browser is out of date!

Update your browser to view this website correctly. Update my browser now

×