当前位置:   article > 正文

Spring Cloud Eureka 全方位深入解析_spring-cloud-starter-netflix-eureka-client 为啥获取的10

spring-cloud-starter-netflix-eureka-client 为啥获取的10.0.0.1

前言:

spring-cloud-starter-netflix-eureka 和 eureka的区别: 前者是对后者的封装,以保证其在spring cloud中的运行。后者为纯粹的servlet应用,需要打war运行。

注:netflix   eureka 2.x 目前已停更

一.介绍

1.eureka介绍:略

2.CAP原理

在分布式环境中,存在一个著名的CAP原理,C-数据一致性;A-服务可用性;P-服务对网络分区故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。

eureka 注册中心符合AP,而zookeeper则符合CP,具体两者的区别会在最后常见问题下进行详细说明。

一.参数配置

1.eureka server 配置

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

1.1 yml配置 (此配置并未囊括所有,如需要其他则自行配置)

  1. #eureka server刷新readCacheMap的时间,注意,client读取的是readCacheMap,这个时间决定了多久会把readWriteCacheMap的缓存更新到readCacheMap上
  2. #默认30s
  3. eureka.server.responseCacheUpdateIntervalMs=3000
  4. #eureka server缓存readWriteCacheMap失效时间,这个只有在这个时间过去后缓存才会失效,失效前不会更新,过期后从registry重新读取注册服务信息,registry是一个ConcurrentHashMap。
  5. #由于启用了evict其实就用不太上改这个配置了
  6. #默认180s
  7. eureka.server.responseCacheAutoExpirationInSeconds=180
  8. #启用主动失效,并且每次主动失效检测间隔为3s
  9. #默认60s
  10. eureka.server.eviction-interval-timer-in-ms=3000
  11. #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除
  12. #注意,EurekaServer一定要设置eureka.server.eviction-interval-timer-in-ms否则这个配置无效,这个配置一般为服务刷新时间配置的三倍
  13. #默认90s
  14. eureka.instance.lease-expiration-duration-in-seconds=15
  15. #服务刷新时间配置,每隔这个时间会主动心跳一次
  16. #默认30s
  17. eureka.instance.lease-renewal-interval-in-seconds=5
  18. #eureka client刷新本地缓存时间
  19. #默认30s
  20. eureka.client.registryFetchIntervalSeconds=5
  21. #eureka客户端ribbon刷新时间
  22. #默认30s
  23. ribbon.ServerListRefreshInterval=1000
  24. eureka.instance.preferIpAddress=true
  25. #关闭自我保护
  26. eureka.server.enable-self-preservation=false
  27. eureka.client.serviceUrl.defaultZone=http://localhost:8211/eureka/,http://localhost:8211/eureka/

1.2 main启动注解

  1. package com.my.test.eureka;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
  5. @EnableEurekaServer //开启eureka服务
  6. @SpringBootApplication
  7. public class TestApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(TestApplication .class, args);
  10. }
  11. }

2.eureka client 配置

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  4. </dependency>

2.1 yml 配置 (此配置并未囊括所有,如需要其他则自行配置)

  1. #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除
  2. #注意,EurekaServer一定要设置eureka.server.eviction-interval-timer-in-ms否则这个配置无效,这个配置一般为服务刷新时间配置的三倍
  3. #默认90s
  4. eureka.instance.lease-expiration-duration-in-seconds=15
  5. #服务刷新时间配置,每隔这个时间会主动心跳一次
  6. #默认30s
  7. eureka.instance.lease-renewal-interval-in-seconds=5
  8. #eureka client刷新本地缓存时间
  9. #默认30s
  10. eureka.client.registryFetchIntervalSeconds=5
  11. #eureka客户端ribbon刷新时间
  12. #默认30s
  13. ribbon.ServerListRefreshInterval=1000
  14. eureka.instance.preferIpAddress=true
  15. #关闭自我保护
  16. eureka.server.enable-self-preservation=false
  17. #最好每个实例不同顺序,按照离自己最近的Eureka地址放到最前面
  18. eureka.client.serviceUrl.defaultZone=http://localhost:8211/eureka/,http://localhost:8211/eureka/

2.2 main 配置

  1. package com.my.test.config;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.config.server.EnableConfigServer;
  5. import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  6. @EnableEurekaClient //此处也可以用 @EnableDiscoveryClient 替代
  7. @SpringBootApplication
  8. public class TestApplication {
  9. public static void main(String[] args) {
  10. SpringApplication.run(TestApplication .class, args);
  11. }
  12. }

二.运行流程

1.服务调用关系

服务提供者

1.1 启动后,向注册中心发起register请求,注册服务

1.2 在运行过程中,定时向注册中心发送renew心跳,证明“我还活着”。

1.3 停止服务提供者,向注册中心发起cancel请求,清空当前服务注册信息。

服务消费者

1.4 启动后,从注册中心拉取服务注册信息

1.5 在运行过程中,定时更新服务注册信息。

1.6 服务消费者发起远程调用:

2.服务续约

Application Service内的Eureka Client后台启动一个定时任务,跟Eureka Server保持一个心跳续约任务,每隔一段时间(默认30S)向Eureka Server发送一次renew请求,进行续约,告诉Eureka Server我还活着,防止被Eureka Server的Evict任务剔除。

3.服务下线

Application Service应用停止后,向Eureka Server发送一个cancel请求,告诉注册中心我已经退出了,Eureka Server接收到之后会将其移出注册列表,后面再有获取注册服务列表的时候就获取不到了,防止消费端消费不可用的服务。

4.服务剔除

Eureka Server启动后在后台启动一个Evict任务,对一定时间内没有续约的服务进行剔除。

三.数据存储结构

1.接口服务层

负责接收并转换对象

2.二级缓存层

2.1.一级缓存

无过期时间,保存服务信息的对外输出数据结构。

2.2.二级缓存

本质上是guava的缓存,包含失效机制,保存服务信息的对外输出数据结构。

3.数据存储层

包含了服务详情和服务治理相关的属性

 

四.附属功能

1.身份验证

注册eureka 的时候,通过Spring Security(注:旧版本和新版本的配置会在差异)增加账号密码验证。

导入jar包

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

增加Spring Security 配置类(注:再此类中也可以增加配置其他的安全操作,比如失败或成功等之后的一些处理逻辑,有兴趣的可以深入学习Spring Security)

  1. package com.my.test.eureka.config;
  2. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  3. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  4. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  5. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  6. /**
  7. * 注册中心(带-账号安全)
  8. * @ClassName SecurityConfig
  9. * @Description
  10. * @Auther Hari
  11. * @Date 2019/1/21 9:35
  12. * @Version 1.0
  13. **/
  14. @EnableWebSecurity
  15. @EnableGlobalMethodSecurity(prePostEnabled = true)
  16. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  17. @Override
  18. protected void configure(HttpSecurity http) throws Exception {
  19. http.csrf().disable();
  20. super.configure(http);
  21. }
  22. }

yml中增加账号密码(账号密码也可以在上面的配置类中进行设置)

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

配置完成后,访问注册中心的网页就可以看到提示输入账号密码了。

服务注册到eureka上的url则如下

http://账号:密码@host:port/eureka

2.健康检查

默认情况下,Eureka使用客户端心跳来确定客户端是否启动。除非另有规定,否则发现客户端将不会根据Spring Boot执行器传播应用程序的当前运行状况检查状态。这意味着成功注册后Eureka将永远宣布申请处于“UP”状态。通过启用Eureka运行状况检查可以改变此行为,从而将应用程序状态传播到Eureka。因此,每个其他应用程序将不会在“UP”之外的状态下将流量发送到应用程序。

开启健康检查

  1. eureka:
  2. client:
  3. healthcheck:
  4. enabled: true

3.状态页和健康指标

  1. eureka:
  2. instance:
  3. status-page-url-path: /actuator/info #eureka注册中心的url link
  4. health-check-url-path: /actuator/health #健康检查的url

4.负载均衡

 

5.高可用性,区域和地区

当存在多个机房时,同一个机房的服务调同一个机房的服务,如同一个机房的服务调不通,则再调用其他机房的服务。

配置

Server 1

  1. eureka:
  2. instance:
  3. metadata-map:
  4. zone: zone-1 #服务所属zone
  5. client:
  6. prefer-same-zone-eureka: true
  7. region: shenzhen #地区
  8. availability-zones:
  9. shenzhen: zone-1,zone-2 #机房12
  10. service-url:
  11. zone-1: http://localhost:8761/eureka/
  12. zone-2: http://localhost:8762/eureka/

Server 2

  1. eureka:
  2. instance:
  3. metadata-map:
  4. zone: zone-2 #服务所属zone
  5. client:
  6. prefer-same-zone-eureka: true
  7. region: shenzhen #地区
  8. availability-zones:
  9. shenzhen: zone-2,zone-1 #机房21
  10. service-url:
  11. zone-1: http://localhost:8761/eureka/
  12. zone-2: http://localhost:8762/eureka/

Client 1

  1. eureka:
  2. instance:
  3. metadata-map:
  4. zone: zone-1 #服务所属zone
  5. client:
  6. prefer-same-zone-eureka: true
  7. region: shenzhen #地区
  8. availability-zones:
  9. shenzhen: zone-1,zone-2 #机房12
  10. service-url:
  11. zone-1: http://localhost:8761/eureka/
  12. zone-2: http://localhost:8762/eureka/

根据如上配置,则client 会先调用server1,如不通则再次调用server2

6.ip地址偏好

在某些情况下,Eureka优先发布服务的IP地址而不是主机名。将eureka.instance.preferIpAddress设置为true,并且当应用程序向eureka注册时,它将使用其IP地址而不是其主机名。

7.同行意识

通过运行多个实例并请求他们相互注册,可以使Eureka更具弹性和可用性。事实上,这是默认的行为,所以你需要做的只是为对方添加一个有效的serviceUrl,例如

  1. ---
  2. spring:
  3. profiles: peer1
  4. eureka:
  5. instance:
  6. hostname: peer1
  7. client:
  8. serviceUrl:
  9. defaultZone: http://peer2/eureka/
  10. ---
  11. spring:
  12. profiles: peer2
  13. eureka:
  14. instance:
  15. hostname: peer2
  16. client:
  17. serviceUrl:
  18. defaultZone: http://peer1/eureka/

8.独立模式

只要存在某种监视器或弹性运行时间(例如Cloud Foundry),两个高速缓存(客户机和服务器)和心跳的组合使独立的Eureka服务器对故障具有相当的弹性。在独立模式下,您可能更喜欢关闭客户端行为,因此不会继续尝试并且无法访问其对等体。例

  1. server:
  2. port: 8761
  3. eureka:
  4. instance:
  5. hostname: localhost
  6. client:
  7. registerWithEureka: false
  8. fetchRegistry: false
  9. serviceUrl:
  10. defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

请注意,serviceUrl指向与本地实例相同的主机。

9.自我保护机制

自我保护机制的工作机制是如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:

1、Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。

2、Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用

3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

配置

  1. eureka:
  2. server:
  3. enable-self-preservation: false #开启或者禁用自我保护
  4. eviction-interval-timer-in-ms: 3000 #心跳检测时间

四.集群部署

简单的多eureka配置(可采取多区域多机房配置,具体参考上:5.高可用性,区域和地区)

server 1

  1. spring:
  2. profiles: peer1
  3. eureka:
  4. instance:
  5. hostname: peer1
  6. client:
  7. serviceUrl:
  8. defaultZone: http://peer2/eureka/

server 2

  1. spring:
  2. profiles: peer2
  3. eureka:
  4. instance:
  5. hostname: peer2
  6. client:
  7. serviceUrl:
  8. defaultZone: http://peer1/eureka/

client 1

  1. eureka:
  2. instance:
  3. lease-renewal-interval-in-seconds: 5
  4. lease-expiration-duration-in-seconds: 15
  5. prefer-ip-address: true
  6. client:
  7. serviceUrl:
  8. defaultZone: http://peer2/eureka/,http://peer1/eureka/ #同时注册多个注册中心

五.其他

1.实现对eureka监听

  1. package com.my.test.eureka.listener;
  2. import com.netflix.appinfo.InstanceInfo;
  3. import com.netflix.discovery.shared.Applications;
  4. import com.netflix.eureka.EurekaServerContextHolder;
  5. import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.joda.time.DateTime;
  8. import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
  9. import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
  10. import org.springframework.context.ApplicationEvent;
  11. import org.springframework.context.ApplicationListener;
  12. import org.springframework.context.annotation.Configuration;
  13. import org.springframework.scheduling.annotation.EnableScheduling;
  14. import org.springframework.scheduling.annotation.Scheduled;
  15. import java.util.Date;
  16. import java.util.concurrent.ConcurrentHashMap;
  17. /**
  18. * @ClassName EurekaListener
  19. * @Description 监听eureka 服务注册情况
  20. * @Auther Hari
  21. * @Date 2019/1/21 10:55
  22. * @Version 1.0
  23. **/
  24. @Configuration
  25. @Slf4j
  26. @EnableScheduling
  27. public class EurekaListener implements ApplicationListener {
  28. private ConcurrentHashMap<String,LostInstance> lostInstanceMap = new ConcurrentHashMap<>();
  29. private int defalutNotifyInterval[] = {0,60,120,240,480};
  30. @Override
  31. public void onApplicationEvent(ApplicationEvent applicationEvent) {
  32. // 如果服务退出
  33. if (applicationEvent instanceof EurekaInstanceCanceledEvent) {
  34. // 对应执行
  35. EurekaInstanceCanceledEvent event = (EurekaInstanceCanceledEvent) applicationEvent;
  36. //获取eureka 注册的服务列表
  37. PeerAwareInstanceRegistry registry = EurekaServerContextHolder.getInstance().getServerContext().getRegistry();
  38. Applications applications = registry.getApplications();
  39. applications.getRegisteredApplications().forEach((registeredApplication) -> {
  40. registeredApplication.getInstances().forEach((instance) -> {
  41. //获取对应的服务信息
  42. if (instance.getInstanceId().equals(event.getServerId())) {
  43. String id = instance.getInstanceId();
  44. log.info("服务ID:{},服务已退出!",id);
  45. //放入退出队列
  46. lostInstanceMap.remove(id);
  47. lostInstanceMap.put(id, new LostInstance(instance));
  48. }
  49. });
  50. });
  51. }
  52. //服务注册
  53. if (applicationEvent instanceof EurekaInstanceRegisteredEvent) {
  54. EurekaInstanceRegisteredEvent event = (EurekaInstanceRegisteredEvent) applicationEvent;
  55. //则移出退出队列
  56. log.info("服务ID:{},服务注册成功!",event.getInstanceInfo().getInstanceId());
  57. lostInstanceMap.remove(event.getInstanceInfo().getInstanceId());
  58. }
  59. }
  60. @Scheduled(cron = "0/30 * * * * ?")
  61. private void notifyLostInstance(){
  62. lostInstanceMap.entrySet().forEach((lostInstanceMap)->{
  63. String key = lostInstanceMap.getKey();
  64. LostInstance lostInstance = lostInstanceMap.getValue();
  65. DateTime dt = new DateTime(lostInstance.getLostTime());
  66. if(dt.plusSeconds(defalutNotifyInterval[lostInstance.getCurrentInterval()]).isBeforeNow()){
  67. log.info("服务:{}已失效,IP为:{},失效时间为:{},请马上重启服务!",new Object[]{lostInstance.getInstanceId(),lostInstance.getIPAddr(),dt.toString()});
  68. }
  69. });
  70. }
  71. class LostInstance extends InstanceInfo {
  72. protected int currentInterval = 0;
  73. protected Date lostTime;
  74. public LostInstance(InstanceInfo ii) {
  75. super(ii);
  76. this.lostTime = new Date();
  77. }
  78. public Date getLostTime() {
  79. return lostTime;
  80. }
  81. public void setLostTime(Date lostTime) {
  82. this.lostTime = lostTime;
  83. }
  84. public int getCurrentInterval(){
  85. return currentInterval++%4;
  86. }
  87. }
  88. }

六.常见问题

1.服务器上多网卡的情况下Eureka的部署问题 (详细:https://blog.csdn.net/neosmith/article/details/53126924

场景:服务器上分别配置了eth0, eth1和eth2三块网卡,只有eth1的地址可供其它机器访问,eth0和eth2的 IP 无效。在这种情况下,服务注册时Eureka Client会自动选择eth0作为服务ip, 导致其它服务无法调用。

解决方案:

1.1. 通过上面源码分析可以得知,spring cloud肯定能配置一个网卡忽略列表。通过查文档资料得知确实存在该属性:

spring.cloud.inetutils.ignored-interfaces[0]=eth0 # 忽略eth0, 支持正则表达式

因此,第一种方案就是通过配置application.properties让应用忽略无效的网卡。

1.2.当网查遍历逻辑都没有找到合适ip时会走JDK的InetAddress.getLocalHost()。该方法会返回当前主机的hostname, 然后会根据hostname解析出对应的ip。因此第二种方案就是配置本机的hostname和/etc/hosts文件,直接将本机的主机名映射到有效IP地址。

1.3.添加以下配置:

  1. # 指定此实例的ip
  2. eureka.instance.ip-address=
  3. # 注册时使用ip而不是主机名
  4. eureka.instance.prefer-ip-address=true

2.Eureka服务注册慢的问题

作为一个实例也包括定期心跳到注册表(通过客户端的serviceUrl),默认持续时间为30秒。在实例,服务器和客户端在其本地缓存中都具有相同的元数据(因此可能需要3个心跳)之前,客户端才能发现服务。您可以使用eureka.instance.leaseRenewalIntervalInSeconds更改期限,这将加快客户端连接到其他服务的过程。在生产中,最好坚持使用默认值,因为服务器内部有一些计算可以对租赁更新期进行假设。

3.本机Netflix EurekaClient的替代方案

可使用@EnableDiscoveryClient注解

你不必使用原始的Netflix EurekaClient,通常使用一个包装器会更方便。Spring Cloud提供Fegin(REST客户端构建器)和Spring RestTemplate去使用Eureka service的逻辑标识符替代物理URLS。配置带固定的物理服务器集合的Ribbon,你可以简单的设置.ribbon.listOfServers的物理服务器地址(或者hostname)集合,并使用逗号分隔符分开,是客户端的ID。

你也可以使用 org.springframework.cloud.client.discoveryClient,它提供了一个简单的API而不是特定于Netflix。

在类中如下

  1. @Autowired
  2. private DiscoveryClient discoveryClient;
  3. public String serviceUrl(){
  4. List<ServiceInstane> list = discoveryClient.getInstances("STORES");
  5. if(list!=null && list.siz()>0) {
  6. return list.get(0).getUri();
  7. }
  8. return null;
  9. }

4.eureka 和zookeeper的区别 (详:https://www.cnblogs.com/springsource/p/9379275.htm )

根据CAP原理,eureka属于AP可用性强,zookeeper属于CP为强一致性。在zookeeper出现故障时,会重新选择leader,这需要时间 ,在此期间服务不可用,而且少于三个后,服务同样不可用。

而eureka只要大于等于一个,就可以继续提供服务,并且不存在选择leader等情况,可以立即切换注册中心。

写的不好,还请见谅。部分内容来源网络。如有意见留言交流。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号