Eureka 介绍

1 关于Eureka

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

1.1 Eureka组成

Eureka包含两个组件:Eureka Server和Eureka Client。

1.2 Eureka Server

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

1.3 Eureka Client

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


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

2. Eureka 不再维护怎么办

  1. Eureka 本身性能很高,及时不维护也不影响你继续使用Spring Cloud
  2. 即使Eureka不满住你的业务需求,有替代方案,那就是Consul和Zookeeper,进阶篇会告诉大家如何替换。

    Eureka服务端搭建

    1. 新建fw-register-eureka服务端

    新建一个fw-register-eureka的服务作为服务端,在pom文件中加入Spring Cloud 的依赖,如下代码清单
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <parent>
    6. <artifactId>fw-cloud-register</artifactId>
    7. <groupId>com.yisu.cloud</groupId>
    8. <version>1.0-SNAPSHOT</version>
    9. </parent>
    10. <modelVersion>4.0.0</modelVersion>
    11. <artifactId>fw-register-eureka</artifactId>
    12. <dependencies>
    13. <dependency>
    14. <groupId>org.springframework.boot</groupId>
    15. <artifactId>spring-boot-starter-web</artifactId>
    16. </dependency>
    17. <dependency>
    18. <groupId>org.springframework.cloud</groupId>
    19. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    20. </dependency>
    21. </dependencies>
    22. </project>

    1.1 编写项目启动类

    1. /**
    2. *
    3. * @Author xuyisu
    4. * @Date 2019/12/6
    5. */
    6. @EnableEurekaServer
    7. @SpringBootApplication
    8. public class FwRegisterEurekaApplication {
    9. public static void main(String[] args) {
    10. SpringApplication.run(FwRegisterEurekaApplication.class, args);
    11. }
    12. }
    启动类与之前介绍的Spring Boot 项目的启动类一致,只是多加了一个@EnableEurekaServer用于声明Eureka服务端,添加配置文件application.yml并设置端口8761,右键启动
  • 报错
    com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
  • 浏览器输入http://localhost:8761/
    注册中心-Eureka - 图1
    显示UNKNOW的原因是没设置服务名,
  • 设置服务名重新启动

    1. server:
    2. port: 8761
    3. spring:
    4. application:
    5. name: fw-register-eureka

    启动仍然报错,但是此时服务名称显示出来了
    注册中心-Eureka - 图2

    1.2 处理Connection refused: connect

    上面说到启动时报错com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
    主要时因为服务器在启动时,会把自己当做一个客户端去注册Eureka服务器,并且会到Eureka服务器中抓取注册信息,它自己本身只是一个服务器而不是服务器的提供者,因此可以修改application.yml文件,修改配置如下

    1. eureka:
    2. client:
    3. register-with-eureka: false
    4. fetch-registry: false
  • register-with-eureka 属性 声明是否将自己的注册信息注册到Eureka服务器,默认值true.属性fetch-registry 表示是否到Eureka服务器中抓取注册信息,将这两个属性设置为false,启动就不会出现异常。
    注册中心-Eureka - 图3

Eureka服务端集群搭建


本节代码地址

GitHub: https://github.com/xuyisu/fw-sping-cloud/tree/master/fw-cloud-register/fw-register-eureka


前面讲到了Eureka 服务端的搭建,选择将其改造成集群的模式,代码还是之前的模块fw-register-eureka,下面我们说如何在不改变代码的情况,仅仅调整配置就可以实现集群部署。

1. 集群结构

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

2. 项目修改

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

  1. server:
  2. port: 8761
  3. spring:
  4. application:
  5. name: fw-register-eureka
  6. eureka:
  7. client:
  8. register-with-eureka: false
  9. fetch-registry: false
  10. #添加注册信息,多个用逗号拼接
  11. service-url:
  12. defaultZone: http://localhost:8762/eureka

2.2 启动8761 Eureka服务

  1. 2019-12-21 12:02:57 INFO main org.springframework.boot.web.embedded.tomcat.TomcatWebServer Tomcat started on port(s): 8761 (http) with context path ''

2.3下来,修改配置的端口

修改上面同一个项目的端口,和注册的eureka服务的端口

  1. server:
  2. port: 8762
  3. spring:
  4. application:
  5. name: fw-register-eureka
  6. eureka:
  7. client:
  8. register-with-eureka: false
  9. fetch-registry: false
  10. service-url:
  11. defaultZone: http://localhost:8761/eureka

2.2 启动8762 Eureka服务

启动之前,修改一下Idea 中的一个配置
注册中心-Eureka - 图5
勾选一下下图的内容
注册中心-Eureka - 图6
完成之后右键启动,可以看到同样的项目启动的两个端口
注册中心-Eureka - 图7
浏览器访问http://localhost:8761/http://localhost:8762/发现都没注册服务.
注册中心-Eureka - 图8
下面我们看一下两个Eureka是否相互已经注册
register-with-eureka属性注释掉,默认是开启的

  1. eureka:
  2. client:
  3. #register-with-eureka: false

然后按照之前的步骤重启

  1. com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
  2. at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187)
  3. at com.sun.jersey.api.client.filter.GZIPContentEncodingFilter.handle(GZIPContentEncodingFilter.java:123)
  4. at com.netflix.discovery.EurekaIdentityHeaderFilter.handle(EurekaIdentityHeaderFilter.java:27)
  5. at com.sun.jersey.api.client.Client.handle(Client.java:652)
  6. at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
  7. at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
  8. at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:570)
  9. at com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient.register(AbstractJerseyEurekaHttpClient.java:56)
  10. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59)
  11. at com.netflix.discovery.shared.transport.decorator.MetricsCollectingEurekaHttpClient.execute(MetricsCollectingEurekaHttpClient.java:73)
  12. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56)
  13. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59)
  14. at com.netflix.discovery.shared.transport.decorator.RedirectingEurekaHttpClient.executeOnNewServer(RedirectingEurekaHttpClient.java:118)
  15. at com.netflix.discovery.shared.transport.decorator.RedirectingEurekaHttpClient.execute(RedirectingEurekaHttpClient.java:79)
  16. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56)
  17. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59)
  18. at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:120)
  19. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56)
  20. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59)
  21. at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77)
  22. at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56)
  23. at com.netflix.discovery.DiscoveryClient.register(DiscoveryClient.java:847)
  24. at com.netflix.discovery.InstanceInfoReplicator.run(InstanceInfoReplicator.java:121)
  25. at com.netflix.discovery.InstanceInfoReplicator$1.run(InstanceInfoReplicator.java:101)
  26. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  27. at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
  28. at java.util.concurrent.FutureTask.run(FutureTask.java)
  29. at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
  30. at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
  31. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  32. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  33. at java.lang.Thread.run(Thread.java:748)

这个报错可以忽略,因为另一个还没起,所以没连接上,讲另一个启动起来就可以了。
下面我们在浏览器访问http://localhost:8761/http://localhost:8762/
注册中心-Eureka - 图9
验证没问题

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


参数 描述 默认
eureka.server.eviction-interval-timer-in-ms 清理无效节点的时间间隔 默认60秒
eureka.dashboard.enabled 是否开启仪表盘,默认true 默认true
eureka.dashboard.path 仪表盘访问路径 默认/
eureka.server.enable-self-preservation 是否开启自我保护 默认true
fetch-registry server 获取注册表 默认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-region eureka服务器中获取的内容是否在远程地区被压缩 TRUE
eureka.server.json-codec-name 如果没有设置默认的编解码器将使用全JSON编解码器,获取的是编码器的类名称 -
eureka.server.list-auto-scaling-groups-role-name 用来描述从AWS第三账户的自动缩放组中的角色名称 ListAutoScalingGroups
eureka.server.log-identity-headers Eureka服务器是否应该登录clientAuthHeaders TRUE
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-seconds http连接被清理之后服务器的空闲时间,单位秒 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连接被清理之后远程地区服务器的空闲时间,单位秒 30
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

2.1 配置来源

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客户端搭建


本节代码地址

GitHub: https://github.com/xuyisu/fw-sping-cloud/tree/master/fw-cloud-client/fw-cloud-client-eureka


在前面的章节中已经在用SpringBoot 构建了一个简单的web工程,并且在里面写了一个简单的RESTFUL服务,本节的客户端搭建,与上个案例类似

1. 新建fw-cloud-client-eureka客户端

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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>fw-cloud-client</artifactId>
  7. <groupId>com.yisu.cloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>fw-cloud-client-eureka</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-web</artifactId>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework.cloud</groupId>
  19. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  20. </dependency>
  21. </dependencies>
  22. </project>

然后在src/man/resources目录中建立application.yml文件,内容如下

  1. server:
  2. port: 8762
  3. spring:
  4. application:
  5. name: fw-register-eureka-client
  6. eureka:
  7. client:
  8. service-url:
  9. defaultZone: http://localhost:8761/eureka

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

2. 编写启动类及RESTFUL接口

2.1 新建启动类

  1. @EnableDiscoveryClient
  2. @SpringBootApplication
  3. public class FwClientEurekaApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(FwClientEurekaApplication.class, args);
  6. }
  7. }

在启动类中使用了@EnableDiscoveryClient注解,该声明表明这是一个Eureka客户端。配置完成后,接着编写RESTFUL接口

2.1 定义实体

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class User implements Serializable {
  5. /**
  6. * 主键
  7. */
  8. private long id;
  9. /**
  10. * 用户名
  11. */
  12. private String username;
  13. /**
  14. * 真实姓名
  15. */
  16. private String realname;
  17. /**
  18. * 邮箱
  19. */
  20. private String email;
  21. /**
  22. * 备注
  23. */
  24. private String remark;
  25. }

@Data 是lombok提供的默认getter setter 注解
@AllArgsConstructor 全部参数的有参构造函数注解
@NoArgsConstructor 无参构造函数注解

2.2 Sevice接口

  1. public interface UserService {
  2. /**
  3. * 模拟数据库获取所有用户
  4. * @return
  5. */
  6. List<User> getUsers();
  7. /**
  8. * 模拟数据库根据id获取用户
  9. * @return
  10. */
  11. User getUserById(long id);
  12. }

2.3 Sevice实现类

  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Override
  4. public List<User> getUsers() {
  5. return initUser();
  6. }
  7. @Override
  8. public User getUserById(long id) {
  9. List<User> userList = getUsers().stream().filter(user -> user.getId() == id).collect(Collectors.toList());
  10. if(CollectionUtils.isEmpty(userList)){
  11. return new User(0,null,null,null,"这位顾客先拿一下腰牌!");
  12. }
  13. return userList.get(0);
  14. }
  15. /**
  16. * 模拟数据库初始化数据
  17. * @return
  18. */
  19. private List<User> initUser(){
  20. List<User> userList =new ArrayList<>();
  21. User user1=new User(1,"113445","刘备","liubei@gmail.com","汉室刘皇叔,蜀国大佬");
  22. User user2=new User(2,"123456","关羽","guanyu@gmail.com","人称关二爷,蜀国五虎上将");
  23. User user3=new User(3,"147258","张飞","zhangfei@gmail.com","此人性格暴躁,蜀国五虎上将");
  24. userList.add(user1);
  25. userList.add(user2);
  26. userList.add(user3);
  27. return userList;
  28. }
  29. }

2.4 控制层

  1. @RestController
  2. public class TestController {
  3. @Autowired
  4. private UserService userService;
  5. /**
  6. * 根据id获取用户
  7. * @param id
  8. * @return
  9. */
  10. @GetMapping("/{id:\\d+}")
  11. public User getUserById(@PathVariable Long id){
  12. return userService.getUserById(id);
  13. }
  14. /**
  15. * 获取全部用户
  16. * @return
  17. */
  18. @GetMapping
  19. public List<User> getUsers(){
  20. return userService.getUsers();
  21. }
  22. @GetMapping("/hello")
  23. public String hello() {
  24. return "hello";
  25. }
  26. }

启动项目,在浏览器输入[http://localhost:8761/](http://localhost:8761/)
注册中心-Eureka - 图10
大家可能看到Eureka 显示一个红色的文字

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

解决办法除了上述关闭知我保护外,哈可以生产上可以开自注册,部署多个server(一般2个以上)

2.5 接口测试

post输入localhost:8762/1
注册中心-Eureka - 图12

3.连接集群的方式

客户端连接集群的方式比较简单,方式如下

3.1 修改客户端项目fw-cloud-client-eureka配置

  1. server:
  2. port: 8763 #如有冲突,修改一下端口
  3. spring:
  4. application:
  5. name: fw-register-eureka-client
  6. eureka:
  7. client:
  8. service-url:
  9. defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka

3.2 启动项目

可直接通过Eureka是否注册成功判断项目是否启动成功
注册中心-Eureka - 图13

3.3 测试接口-get请求

post输入localhost:8763/1
注册中心-Eureka - 图14

Eureka 密码访问


为了保护Eureka 的服务器的安全性,不行被其它微服务注册,我们可以对Eureka 设置一层密码,使只有知道Eureka 账号密码的才可以连接,当然如果是公司内网的话,可以不设置密码,那么如何实现呢,下面就来演示。

1.修改Eureka配置

修改fw-register-eureka注册服务的maven,添加已下依赖,至于Security 的其他使用功能,后续章节会讲到。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>

2.修改Eureka应用配置

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

  1. spring:
  2. security:
  3. user:
  4. name: fwcloud
  5. password: 123455

3.添加Security配置类

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

  1. @Configuration
  2. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  3. @Override
  4. protected void configure(HttpSecurity http) throws Exception {
  5. http.httpBasic().and().sessionManagement()
  6. .sessionCreationPolicy(SessionCreationPolicy.NEVER)
  7. .and().authorizeRequests().anyRequest().authenticated()
  8. .and().csrf().disable();
  9. }
  10. }

2.重启Eureka

浏览器输入http://localhost:8761会弹出一个登录框
注册中心-Eureka - 图15
输入用户名fwcloud,密码123456即可跳转我们熟悉的页面

3.客户端注册

客户端做简要修改即可

  1. eureka:
  2. client:
  3. service-url:
  4. defaultZone: http://fwcloud:123456@localhost:8761/eureka

4. 启动客户端

可以看到,已经成功注册上来了
注册中心-Eureka - 图16

服务健康检查


本节代码地址

GitHub: https://github.com/xuyisu/fw-sping-cloud/tree/master/fw-cloud-actuator


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

  • 场景一
    比如一个和第三方系统对接的服务,第三方系统可能已经挂了或者早已失效,客户端应当告诉服务端当前第三方系统的状态,实现该功能,可以使用Eureka的健康检查控制器。
  • 场景二
    我们需要在对外提供的服务升级的时候,如果新发的服务发送成功,让老的系统处理完业务慢慢下线,所有调用此服务的应用全部走新发的引用。
  • 场景其它
    其它需要对注册上Eureka 服务器上的手里做手动下线和上线的操作

    1.Sring Boot Actuator的使用

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

    1.1 新建项目

    fw-cloud-actuator项目中pom文件引入spring-boot-starter-actuator包,并添加pom.xml文件如下:

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-web</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.springframework.cloud</groupId>
    8. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    9. </dependency>
    10. <dependency>
    11. <groupId>org.springframework.boot</groupId>
    12. <artifactId>spring-boot-starter-actuator</artifactId>
    13. </dependency>
    14. </dependencies>

    1.2 创建启动类

    1. @EnableDiscoveryClient
    2. @SpringBootApplication
    3. public class FwActuatorApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(FwActuatorApplication.class, args);
    6. }
    7. }

    1.3 添加项目配置

    1. server:
    2. port: 8863
    3. spring:
    4. application:
    5. name: fw-cloud-actuator
    6. eureka:
    7. client:
    8. service-url:
    9. defaultZone: http://localhost:8761/eureka

    1.4 启动fw-cloud-actuator项目

    poatman 请求localhost:8762/actuator/health
    返回结果

    1. {
    2. "status": "UP"
    3. }

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

    2. 实现应用程序自检

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

  • 1.让客户端自己进行检查,是否能连接其他服务

  • 2.将连接第三方服务的结果和客户端的状态关联
    我们使用Spring Boot Cloud 可以直接实现一个自定义的健康检查器。根据能否访问第三方应用,来决定应自己的健康状态。

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

    实现一个自定义的检查器必须要继承HealthIndicator接口,接口中中包含一个默认的实现和一个未实现的接口(需要实现),默认实现的getHealth(boolean includeDetails)可以根据需要自定义实现 ,getHealth所调用的this.health()使我们即将要实现的。
    1. @FunctionalInterface
    2. public interface HealthIndicator extends HealthContributor {
    3. default Health getHealth(boolean includeDetails) {
    4. Health health = this.health();
    5. return includeDetails ? health : health.withoutDetails();
    6. }
    7. Health health();
    8. }

    这里需要注意,default 关键字是Java 8 以上才支持的语法,使接口也可以有自己的默认实现,原因是为了确保与为这些接口的旧版本编写的代码兼容。并且不用在其子类进行逐个实现。

  1. /**
  2. * @author xuyisu
  3. * @description 自定义健康检查器
  4. * @date 2019/12/21
  5. */
  6. @Component
  7. public class MyHealthIndicator implements HealthIndicator {
  8. @Override
  9. public Health health() {
  10. if(HealthStatus.isVisited){
  11. //成功和第三方连接
  12. return new Health.Builder(Status.UP).build();
  13. }else {
  14. //和第三方连接失败
  15. return new Health.Builder(Status.DOWN).build();
  16. }
  17. }
  18. }

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

  1. /**
  2. * @author xuyisu
  3. * @description 健康状态信息
  4. * @date 2019/12/21
  5. */
  6. @Data
  7. public class HealthStatus {
  8. /**
  9. * 是否是可访问的
  10. */
  11. public static boolean isVisited;
  12. }

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

  1. /**
  2. * @author xuyisu
  3. * @description 模拟是否可与第三方访问
  4. * @date 2019/12/21
  5. */
  6. @RestController
  7. @Slf4j
  8. public class MyHealthController {
  9. @PostMapping("/healthStatus")
  10. public ResponseEntity<String> healthStatus(boolean isHealth){
  11. HealthStatus.isVisited=isHealth;
  12. log.info("当前与第三方连接是否可用,{}",isHealth);
  13. return new ResponseEntity<>(HttpStatus.OK);
  14. }
  15. }

2.2 二次启动

通过postman 请求localhost:8863/actuator/health
返回结果
{
“status”: “DOWN”
}
指示器会根据HealthStatus.isVisited判断健康值,如果是true,返回的就是“UP”状态,反之返回“DOWN”,默认状态下HealthStatus.isVisited的值是false,因此健康状态为“DOWN”,如果想设置为“UP”,请求接口localhost:8863/healthStatus
注意参数和请求方式
注册中心-Eureka - 图17
再次通过postman 请求localhost:8863/actuator/health
返回结果
{
“status”: “UP”
}

2.3 健康检查处理器

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

  1. /**
  2. * @author xuyisu
  3. * @description 健康检查处理器
  4. * @date 2019/12/21
  5. */
  6. @Component
  7. @Slf4j
  8. public class MyHealthCheckHandler implements HealthCheckHandler {
  9. @Autowired
  10. private MyHealthIndicator myHealthIndicator;
  11. @Override
  12. public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus instanceStatus) {
  13. if(myHealthIndicator.health().getStatus().equals(Status.UP)){
  14. log.info("第三方服务可以正常访问");
  15. return InstanceInfo.InstanceStatus.UP;
  16. }else{
  17. log.info("第三方服务访问异常");
  18. return InstanceInfo.InstanceStatus.DOWN;
  19. }
  20. }
  21. }

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

2.4 三次启动

浏览器输入http://localhost:8761/可以看到显示的是“DOWN”状态。
注册中心-Eureka - 图18
并且我们的启动日志会看到自定义健康检查器输出的日志信息

  1. 2019-12-21 19:29:26 INFO DiscoveryClient-InstanceInfoReplicator-0 com.yisu.actuator.config.MyHealthCheckHandler 第三方服务访问异常

现在我们通过postman 修改访问第三方应用的状态,可以看到如下结果
注册中心-Eureka - 图19
10秒钟之后浏览器输入http://localhost:8761/可以看到显示的是”UP”
注册中心-Eureka - 图20
控制台输出的日志也变成了

  1. 2019-12-21 19:32:40 INFO http-nio-8863-exec-2 com.yisu.actuator.controller.MyHealthController 当前与第三方连接是否可用,true
  2. 2019-12-21 19:32:46 INFO DiscoveryClient-InstanceInfoReplicator-0 com.yisu.actuator.config.MyHealthCheckHandler 第三方服务可以正常访问
  3. 2019-12-21 19:32:46 WARN DiscoveryClient-InstanceInfoReplicator-0 com.netflix.discovery.DiscoveryClient Saw local status change event StatusChangeEvent [timestamp=1576927966804, current=UP, previous=DOWN]
  4. 2019-12-21 19:32:46 INFO DiscoveryClient-InstanceInfoReplicator-0 com.netflix.discovery.DiscoveryClient DiscoveryClient_FW-CLOUD-ACTUATOR/DESKTOP-375C7DM:fw-cloud-actuator:8863: registering service...
  5. 2019-12-21 19:32:46 INFO DiscoveryClient-InstanceInfoReplicator-0 com.netflix.discovery.DiscoveryClient Discov

Client 端重要参数


1. eureka.client.常用

参数 描述 默认
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-seconds Eureka 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

2. eureka.client.其它

参数 描述 默认
eureka.client.allow-redirects 服务器是否能够重定向客户端请求到备份服务器。 如果设置为false,服务器将直接处理请求,如果设置为true,它可能发送HTTP重定向到客户端。默认为false 默认FALSE
eureka.client.availability-zones 获取此实例所在区域的可用性区域列表(在AWS数据中心中使用)。这些更改在registryFetchIntervalSeconds指定的下一个注册表获取周期的运行时生效,用逗号隔开。
eureka.client.backup-registry-impl 获取实现了eureka客户端在第一次启动时读取注册表的信息作为回退选项的实现名称
eureka.client.cache-refresh-executor-exponential-back-off-bound 执行程序指数回退刷新的相关属性,是重试延迟的最大倍数值,默认为10 默认10
eureka.client.cache-refresh-executor-thread-pool-size 执行程序缓存刷新线程池的大小,默认为2 默认2
eureka.client.client-data-accept EurekaAccept客户端数据接受名称
eureka.client.decoder-name 这是一个短暂的解码器的配置,如果最新的解码器是稳定的,则可以去除,默认为null
eureka.client.disable-delta 指示eureka客户端是否应该禁用提取delta,而应该诉诸于获取完整的注册表信息。请注意,增量获取可以极大地减少流量,因为尤利卡服务器的更改速率通常远低于提取速率。更改在运行时在registryFetchIntervalSeconds指定的下一个注册表提取周期中有效 默认FALSE
eureka.client.dollar-replacement eureka服务器序列化/反序列化的信息中获取“$”符号的的替换字符串。默认为“_-” 默认_-
eureka.client.enabled 用于指示Eureka客户端已启用的标志 默认TRUE
eureka.client.encoder-name 这是一个短暂的编码器的配置,如果最新的编码器是稳定的,则可以去除,默认为null
eureka.client.escape-char-replacement eureka服务器序列化/反序列化的信息中获取“”符号的的替换字符串。默认为“_ 默认 __
eureka.client.eureka-server-d-n-s-name 获取要查询的DNS名称来获得eureka服务器,此配置只有在eureka服务器ip地址列表是在DNS中才会用到。默认为null
eureka.client.eureka-server-port 获取eureka服务器的端口,此配置只有在eureka服务器ip地址列表是在DNS中才会用到。默认为null
eureka.client.eureka-server-u-r-l-context 表示eureka注册中心的路径,如果配置为eureka,则为http://x.x.x.x:x/eureka/,在eureka的配置文件中加入此配置表示eureka作为客户端向注册中心注册,从而构成eureka集群。此配置只有在eureka服务器ip地址列表是在DNS中才会用到,默认为null
eureka.client.fetch-remote-regions-registry eureka服务注册表信息里的以逗号隔开的地区名单,如果不这样返回这些地区名单,则客户端启动将会出错。默认为null
eureka.client.filter-only-up-instances 是否获得处于开启状态的实例的应用程序过滤之后的应用程序。默认为true 默认TRUE
eureka.client.g-zip-content eureka注册表的内容是否被压缩,默认为true,并且是在最好的网络流量下被压缩 默认TRUE
eureka.client.heartbeat-executor-exponential-back-off-bound 心跳执行程序回退相关的属性,是重试延迟的最大倍数值,默认为10 默认10
eureka.client.heartbeat-executor-thread-pool-size 心跳执行程序线程池的大小,默认为2 默认2
eureka.client.log-delta-diff 是否记录eureka服务器和客户端之间在注册表的信息方面的差异,默认为false 默认FALSE
eureka.client.on-demand-update-status-change 如果设置为true,客户端的状态更新将会点播更新到远程服务器上,默认为true 默认TRUE
eureka.client.prefer-same-zone-eureka 实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下 默认TRUE
eureka.client.property-resolver 属性解析器
eureka.client.proxy-host 获取eureka服务的代理主机,默认为null
eureka.client.proxy-password 获取eureka服务的代理密码,默认为null
eureka.client.proxy-port 获取eureka服务的代理端口, 默认为null
eureka.client.proxy-user-name 获取eureka服务的代理用户名,默认为null
eureka.client.region 获取实例所在的地区。默认为us-east-1 默认us-east-1
eureka.client.registry-refresh-single-vip-address 此客户端只对一个单一的VIP注册表的信息感兴趣。默认为null
eureka.client.use-dns-for-fetching-service-urls eureka客户端是否应该使用DNS机制来获取eureka服务器的地址列表,默认为false 默认FALSE

3. eureka.instance.常用

参数 描述 默认
eureka.instance.instance-id 此实例注册到eureka服务端的唯一的实例ID,其组成为${spring.application.name}:${spring.application.instance_id:${random.value}}服务名,默认取 spring.application.name 配置值
eureka.instance.hostname 主机名,不配置的时候讲根据操作系统的主机名来获取 默认unknown
eureka.instance.prefer-ip-address 是否优先使用IP地址作为主机名的标识,如果设置了tru,则使用该属性配置的IP,否则自动获取除环路IP外的第一个IP地址
eureka.instance.ip-address IP地址 默认FALSE
eureka.instance.lease-expiration-duration-in-seconds 定义服务续约任务(心跳)的调用间隔,单位:秒,表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量。 默认90
eureka.instance.lease-renewal-interval-in-seconds 定义服务失效的时间,单位:秒,表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance。该值至少应该大于leaseRenewalIntervalInSeconds 默认30
eureka.instance.home-page-url 获取此实例的绝对主页URL路径,为其他服务提供信息时使用的路径,默认为null
eureka.instance.home-page-url-path 获取此实例的相关主页URL路径,然后构造出主机名,安全端口等,默认为/ 默认 /
eureka.instance.health-check-url 配置健康检查页面的URL,绝对路径
eureka.instance.health-check-url-path 健康检查页面的URL,相对路径,默认使用 HTTP 访问,如果需要使用 HTTPS则需要使用绝对路径 默认/actuator/health
eureka.instance.secure-health-check-url 获取此实例的绝对安全健康检查网页的URL路径,默认为null

4. eureka.instance.其它

参数 描述 默认
a-s-g-name 与此实例相关联 AWS自动缩放组名称。此项配置是在AWS环境专门使用的实例启动,它已被用于流量停用后自动把一个实例退出服务。
app-group-name 注册到注册中心的应用所属分组名称
data-center-info 指定服务实例所属数据中心
default-address-resolution-order 获取实例的网络地址 默认为[]
environment 该服务实例环境配置
initial-status 该服务实例注册到Eureka Server 的初始状态 默认UP
instance-enabled-onit 实例注册到eureka服务器时,是否开启通讯 默认false
metadata-map 获取与此实例相关联的元数据(key,value)。这个信息被发送到eureka服务器,其他实例可以使用。
namespace 获取用于查找属性的命名空间,默认为eureka 默认eureka
non-secure-port 获取该实例应该接收通信的非安全端口。 默认80
non-secure-port-enabled 该实例应该接收通信的非安全端口是否启用 默认true
secure-port 获取该实例应该接收通信的安全端口 默认443
secure-port-enabled 该实例应该接收通信的安全端口是否启用 默认false
secure-virtual-host-name 服务实例安全主机名称(HTTPS) 默认unknown

5. 配置来源

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