赞
踩
spring-cloud-starter-netflix-eureka 和 eureka的区别: 前者是对后者的封装,以保证其在spring cloud中的运行。后者为纯粹的servlet应用,需要打war运行。
注:netflix eureka 2.x 目前已停更
在分布式环境中,存在一个著名的CAP原理,C-数据一致性;A-服务可用性;P-服务对网络分区故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。
eureka 注册中心符合AP,而zookeeper则符合CP,具体两者的区别会在最后常见问题下进行详细说明。
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
- </dependency>
- #eureka server刷新readCacheMap的时间,注意,client读取的是readCacheMap,这个时间决定了多久会把readWriteCacheMap的缓存更新到readCacheMap上
- #默认30s
- eureka.server.responseCacheUpdateIntervalMs=3000
- #eureka server缓存readWriteCacheMap失效时间,这个只有在这个时间过去后缓存才会失效,失效前不会更新,过期后从registry重新读取注册服务信息,registry是一个ConcurrentHashMap。
- #由于启用了evict其实就用不太上改这个配置了
- #默认180s
- eureka.server.responseCacheAutoExpirationInSeconds=180
-
- #启用主动失效,并且每次主动失效检测间隔为3s
- #默认60s
- eureka.server.eviction-interval-timer-in-ms=3000
-
- #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除
- #注意,EurekaServer一定要设置eureka.server.eviction-interval-timer-in-ms否则这个配置无效,这个配置一般为服务刷新时间配置的三倍
- #默认90s
- eureka.instance.lease-expiration-duration-in-seconds=15
- #服务刷新时间配置,每隔这个时间会主动心跳一次
- #默认30s
- eureka.instance.lease-renewal-interval-in-seconds=5
- #eureka client刷新本地缓存时间
- #默认30s
- eureka.client.registryFetchIntervalSeconds=5
- #eureka客户端ribbon刷新时间
- #默认30s
- ribbon.ServerListRefreshInterval=1000
- eureka.instance.preferIpAddress=true
- #关闭自我保护
- eureka.server.enable-self-preservation=false
- eureka.client.serviceUrl.defaultZone=http://localhost:8211/eureka/,http://localhost:8211/eureka/
- package com.my.test.eureka;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
-
- @EnableEurekaServer //开启eureka服务
- @SpringBootApplication
- public class TestApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(TestApplication .class, args);
- }
-
- }
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
- </dependency>
- #服务过期时间配置,超过这个时间没有接收到心跳EurekaServer就会将这个实例剔除
- #注意,EurekaServer一定要设置eureka.server.eviction-interval-timer-in-ms否则这个配置无效,这个配置一般为服务刷新时间配置的三倍
- #默认90s
- eureka.instance.lease-expiration-duration-in-seconds=15
- #服务刷新时间配置,每隔这个时间会主动心跳一次
- #默认30s
- eureka.instance.lease-renewal-interval-in-seconds=5
- #eureka client刷新本地缓存时间
- #默认30s
- eureka.client.registryFetchIntervalSeconds=5
- #eureka客户端ribbon刷新时间
- #默认30s
- ribbon.ServerListRefreshInterval=1000
- eureka.instance.preferIpAddress=true
- #关闭自我保护
- eureka.server.enable-self-preservation=false
- #最好每个实例不同顺序,按照离自己最近的Eureka地址放到最前面
- eureka.client.serviceUrl.defaultZone=http://localhost:8211/eureka/,http://localhost:8211/eureka/
- package com.my.test.config;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.cloud.config.server.EnableConfigServer;
- import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
-
- @EnableEurekaClient //此处也可以用 @EnableDiscoveryClient 替代
- @SpringBootApplication
- public class TestApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(TestApplication .class, args);
- }
-
- }
服务提供者
1.1 启动后,向注册中心发起register请求,注册服务
1.2 在运行过程中,定时向注册中心发送renew心跳,证明“我还活着”。
1.3 停止服务提供者,向注册中心发起cancel请求,清空当前服务注册信息。
服务消费者
1.4 启动后,从注册中心拉取服务注册信息
1.5 在运行过程中,定时更新服务注册信息。
1.6 服务消费者发起远程调用:
Application Service内的Eureka Client后台启动一个定时任务,跟Eureka Server保持一个心跳续约任务,每隔一段时间(默认30S)向Eureka Server发送一次renew请求,进行续约,告诉Eureka Server我还活着,防止被Eureka Server的Evict任务剔除。
Application Service应用停止后,向Eureka Server发送一个cancel请求,告诉注册中心我已经退出了,Eureka Server接收到之后会将其移出注册列表,后面再有获取注册服务列表的时候就获取不到了,防止消费端消费不可用的服务。
Eureka Server启动后在后台启动一个Evict任务,对一定时间内没有续约的服务进行剔除。
负责接收并转换对象
2.1.一级缓存
无过期时间,保存服务信息的对外输出数据结构。
2.2.二级缓存
本质上是guava的缓存,包含失效机制,保存服务信息的对外输出数据结构。
包含了服务详情和服务治理相关的属性
注册eureka 的时候,通过Spring Security(注:旧版本和新版本的配置会在差异)增加账号密码验证。
导入jar包
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
增加Spring Security 配置类(注:再此类中也可以增加配置其他的安全操作,比如失败或成功等之后的一些处理逻辑,有兴趣的可以深入学习Spring Security)
- package com.my.test.eureka.config;
-
- import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
- import org.springframework.security.config.annotation.web.builders.HttpSecurity;
- import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
- /**
- * 注册中心(带-账号安全)
- * @ClassName SecurityConfig
- * @Description
- * @Auther Hari
- * @Date 2019/1/21 9:35
- * @Version 1.0
- **/
- @EnableWebSecurity
- @EnableGlobalMethodSecurity(prePostEnabled = true)
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.csrf().disable();
- super.configure(http);
- }
- }
yml中增加账号密码(账号密码也可以在上面的配置类中进行设置)
- spring:
- security:
- user:
- name: admin
- password: admin
配置完成后,访问注册中心的网页就可以看到提示输入账号密码了。
服务注册到eureka上的url则如下
http://账号:密码@host:port/eureka
默认情况下,Eureka使用客户端心跳来确定客户端是否启动。除非另有规定,否则发现客户端将不会根据Spring Boot执行器传播应用程序的当前运行状况检查状态。这意味着成功注册后Eureka将永远宣布申请处于“UP”状态。通过启用Eureka运行状况检查可以改变此行为,从而将应用程序状态传播到Eureka。因此,每个其他应用程序将不会在“UP”之外的状态下将流量发送到应用程序。
开启健康检查
- eureka:
- client:
- healthcheck:
- enabled: true
- eureka:
- instance:
- status-page-url-path: /actuator/info #eureka注册中心的url link
- health-check-url-path: /actuator/health #健康检查的url
当存在多个机房时,同一个机房的服务调同一个机房的服务,如同一个机房的服务调不通,则再调用其他机房的服务。
配置
Server 1
- eureka:
- instance:
- metadata-map:
- zone: zone-1 #服务所属zone
- client:
- prefer-same-zone-eureka: true
- region: shenzhen #地区
- availability-zones:
- shenzhen: zone-1,zone-2 #机房12
- service-url:
- zone-1: http://localhost:8761/eureka/
- zone-2: http://localhost:8762/eureka/
Server 2
- eureka:
- instance:
- metadata-map:
- zone: zone-2 #服务所属zone
- client:
- prefer-same-zone-eureka: true
- region: shenzhen #地区
- availability-zones:
- shenzhen: zone-2,zone-1 #机房21
- service-url:
- zone-1: http://localhost:8761/eureka/
- zone-2: http://localhost:8762/eureka/
Client 1
- eureka:
- instance:
- metadata-map:
- zone: zone-1 #服务所属zone
- client:
- prefer-same-zone-eureka: true
- region: shenzhen #地区
- availability-zones:
- shenzhen: zone-1,zone-2 #机房12
- service-url:
- zone-1: http://localhost:8761/eureka/
- zone-2: http://localhost:8762/eureka/
根据如上配置,则client 会先调用server1,如不通则再次调用server2
在某些情况下,Eureka优先发布服务的IP地址而不是主机名。将eureka.instance.preferIpAddress设置为true,并且当应用程序向eureka注册时,它将使用其IP地址而不是其主机名。
通过运行多个实例并请求他们相互注册,可以使Eureka更具弹性和可用性。事实上,这是默认的行为,所以你需要做的只是为对方添加一个有效的serviceUrl,例如
- ---
- spring:
- profiles: peer1
- eureka:
- instance:
- hostname: peer1
- client:
- serviceUrl:
- defaultZone: http://peer2/eureka/
-
- ---
- spring:
- profiles: peer2
- eureka:
- instance:
- hostname: peer2
- client:
- serviceUrl:
- defaultZone: http://peer1/eureka/
只要存在某种监视器或弹性运行时间(例如Cloud Foundry),两个高速缓存(客户机和服务器)和心跳的组合使独立的Eureka服务器对故障具有相当的弹性。在独立模式下,您可能更喜欢关闭客户端行为,因此不会继续尝试并且无法访问其对等体。例
- server:
- port: 8761
-
- eureka:
- instance:
- hostname: localhost
- client:
- registerWithEureka: false
- fetchRegistry: false
- serviceUrl:
- defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
请注意,serviceUrl指向与本地实例相同的主机。
自我保护机制的工作机制是如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
1、Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
2、Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用
3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
配置
- eureka:
- server:
- enable-self-preservation: false #开启或者禁用自我保护
- eviction-interval-timer-in-ms: 3000 #心跳检测时间
简单的多eureka配置(可采取多区域多机房配置,具体参考上:5.高可用性,区域和地区)
server 1
- spring:
- profiles: peer1
- eureka:
- instance:
- hostname: peer1
- client:
- serviceUrl:
- defaultZone: http://peer2/eureka/
server 2
- spring:
- profiles: peer2
- eureka:
- instance:
- hostname: peer2
- client:
- serviceUrl:
- defaultZone: http://peer1/eureka/
client 1
- eureka:
- instance:
- lease-renewal-interval-in-seconds: 5
- lease-expiration-duration-in-seconds: 15
- prefer-ip-address: true
- client:
- serviceUrl:
- defaultZone: http://peer2/eureka/,http://peer1/eureka/ #同时注册多个注册中心
- package com.my.test.eureka.listener;
-
- import com.netflix.appinfo.InstanceInfo;
- import com.netflix.discovery.shared.Applications;
- import com.netflix.eureka.EurekaServerContextHolder;
- import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
- import lombok.extern.slf4j.Slf4j;
- import org.joda.time.DateTime;
- import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
- import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
- import org.springframework.context.ApplicationEvent;
- import org.springframework.context.ApplicationListener;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.scheduling.annotation.EnableScheduling;
- import org.springframework.scheduling.annotation.Scheduled;
-
- import java.util.Date;
- import java.util.concurrent.ConcurrentHashMap;
-
- /**
- * @ClassName EurekaListener
- * @Description 监听eureka 服务注册情况
- * @Auther Hari
- * @Date 2019/1/21 10:55
- * @Version 1.0
- **/
- @Configuration
- @Slf4j
- @EnableScheduling
- public class EurekaListener implements ApplicationListener {
-
- private ConcurrentHashMap<String,LostInstance> lostInstanceMap = new ConcurrentHashMap<>();
- private int defalutNotifyInterval[] = {0,60,120,240,480};
-
- @Override
- public void onApplicationEvent(ApplicationEvent applicationEvent) {
- // 如果服务退出
- if (applicationEvent instanceof EurekaInstanceCanceledEvent) {
- // 对应执行
- EurekaInstanceCanceledEvent event = (EurekaInstanceCanceledEvent) applicationEvent;
- //获取eureka 注册的服务列表
- PeerAwareInstanceRegistry registry = EurekaServerContextHolder.getInstance().getServerContext().getRegistry();
- Applications applications = registry.getApplications();
- applications.getRegisteredApplications().forEach((registeredApplication) -> {
- registeredApplication.getInstances().forEach((instance) -> {
- //获取对应的服务信息
- if (instance.getInstanceId().equals(event.getServerId())) {
- String id = instance.getInstanceId();
- log.info("服务ID:{},服务已退出!",id);
- //放入退出队列
- lostInstanceMap.remove(id);
- lostInstanceMap.put(id, new LostInstance(instance));
- }
- });
- });
- }
- //服务注册
- if (applicationEvent instanceof EurekaInstanceRegisteredEvent) {
- EurekaInstanceRegisteredEvent event = (EurekaInstanceRegisteredEvent) applicationEvent;
- //则移出退出队列
- log.info("服务ID:{},服务注册成功!",event.getInstanceInfo().getInstanceId());
- lostInstanceMap.remove(event.getInstanceInfo().getInstanceId());
- }
- }
-
- @Scheduled(cron = "0/30 * * * * ?")
- private void notifyLostInstance(){
- lostInstanceMap.entrySet().forEach((lostInstanceMap)->{
- String key = lostInstanceMap.getKey();
- LostInstance lostInstance = lostInstanceMap.getValue();
- DateTime dt = new DateTime(lostInstance.getLostTime());
- if(dt.plusSeconds(defalutNotifyInterval[lostInstance.getCurrentInterval()]).isBeforeNow()){
- log.info("服务:{}已失效,IP为:{},失效时间为:{},请马上重启服务!",new Object[]{lostInstance.getInstanceId(),lostInstance.getIPAddr(),dt.toString()});
- }
- });
- }
-
- class LostInstance extends InstanceInfo {
- protected int currentInterval = 0;
- protected Date lostTime;
- public LostInstance(InstanceInfo ii) {
- super(ii);
- this.lostTime = new Date();
- }
- public Date getLostTime() {
- return lostTime;
- }
- public void setLostTime(Date lostTime) {
- this.lostTime = lostTime;
- }
- public int getCurrentInterval(){
- return currentInterval++%4;
- }
- }
- }
场景:服务器上分别配置了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.添加以下配置:
- # 指定此实例的ip
- eureka.instance.ip-address=
- # 注册时使用ip而不是主机名
- eureka.instance.prefer-ip-address=true
作为一个实例也包括定期心跳到注册表(通过客户端的serviceUrl),默认持续时间为30秒。在实例,服务器和客户端在其本地缓存中都具有相同的元数据(因此可能需要3个心跳)之前,客户端才能发现服务。您可以使用eureka.instance.leaseRenewalIntervalInSeconds更改期限,这将加快客户端连接到其他服务的过程。在生产中,最好坚持使用默认值,因为服务器内部有一些计算可以对租赁更新期进行假设。
可使用@EnableDiscoveryClient注解
你不必使用原始的Netflix EurekaClient,通常使用一个包装器会更方便。Spring Cloud提供Fegin(REST客户端构建器)和Spring RestTemplate去使用Eureka service的逻辑标识符替代物理URLS。配置带固定的物理服务器集合的Ribbon,你可以简单的设置.ribbon.listOfServers的物理服务器地址(或者hostname)集合,并使用逗号分隔符分开,是客户端的ID。
你也可以使用 org.springframework.cloud.client.discoveryClient,它提供了一个简单的API而不是特定于Netflix。
在类中如下
- @Autowired
- private DiscoveryClient discoveryClient;
-
- public String serviceUrl(){
- List<ServiceInstane> list = discoveryClient.getInstances("STORES");
- if(list!=null && list.siz()>0) {
- return list.get(0).getUri();
- }
- return null;
- }
根据CAP原理,eureka属于AP可用性强,zookeeper属于CP为强一致性。在zookeeper出现故障时,会重新选择leader,这需要时间 ,在此期间服务不可用,而且少于三个后,服务同样不可用。
而eureka只要大于等于一个,就可以继续提供服务,并且不存在选择leader等情况,可以立即切换注册中心。
写的不好,还请见谅。部分内容来源网络。如有意见留言交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。