服务注册发现-Eureka git
•基于 REST 的服务,它主要是用于定位服务,以实现 AWS 云端的负载均衡和中间层服务器的故障转移。若是你但愿实现一个 AP( Availability and Partition ) 系统, Eureka 是一个很好的选择,并在 Netflix 获得了实战的检验。在出现网络分区时, Eureka 选择可用性,而不是一致性github
•默认状况下每一个Eureka服务端也是一个Eureka客户端而且经过请求服务的URL去定位一个节点。若是你不提供的话,服务虽然还会运行和工做,可是它不会向你打印一堆关于没有注册的节点日志。spring
•当一个客户端注册到Eureka,它提供关于本身的元数据(诸如主机和端口,健康指标URL,首页等)以后每 30 秒钟发送心跳以更新自身状态。若是该客户端没能发送心跳更新,它将在 90 秒以后被其注册的 Eureka 服务器剔除。来自任意 zone 的 Application Client 能够查看这些注册信息(每隔 30 秒查看一次)并依此定位本身的依赖应用实例,进而进行远程调用。bootstrap
Eureka Server 配置缓存
POM服务器
Application网络
@SpringBootApplication @EnableEurekaServer public class RegistryApplication extends SpringBootServletInitializer { public static void main(String[] args) throws Exception { SpringApplication.run(RegistryApplication.class, args); } }
这段很简单启动EurekaServer,就不废话
bootstrap.propertiesapp
#系统 spring.application.name=registry server.port=7070 server.context-path=/ server.uri-encoding=utf-8 management.context-path=/management info.app.name=${spring.application.name} info.app.profiles=${spring.profiles.active} info.app.version=@project.version@ #仅供本地访问 management.address=127.0.0.1 spring.profiles.active=@env@
application.properties负载均衡
#eureka 服务端 #本机是否注册服务 eureka.client.registerWithEureka=false #启动时是否检测注册客户端 eureka.client.fetchRegistry=false #启用Ip注册 eureka.instance.perferIpAddress=true #剔除无效实例频率默认60S eureka.server.evictionIntervalTimerInMs=10000 #注册服务地址 eureka.client.serviceUrl.defaultZone=http://localhost:7070/eureka/ eureka.instance.metadataMap.management.context-path=${management.context-path}
Eureka Client dom
@EnableEurekaClient启动客户端
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
eureka.client.serviceUrl.defaultZone=http://localhost:7070/eureka/ #启用Ip注册 eureka.instance.preferIpAddress=true #续约心跳时间 默认三十秒 eureka.instance.leaseRenewalIntervalInSeconds=10 #续约时效时间 eureka.instance.leaseExpirationDurationInSeconds=90 #状态页面 eureka.instance.statusPageUrlPath=${management.context-path}/info #健康检查页面 eureka.instance.healthCheckUrlPath=${management.context-path}/health #turbine配置 eureka.instance.metadataMap.cluster=MAIN eureka.instance.metadataMap.management.context-path=${management.context-path}
默认使用hostname注册
可根据eureka.instance.preferIpAddress=true更改成IP
未设置ipaddr或hostname,默认会从hostInfo获取ipAddr 或 hostname
instanceId 默认值为hostname/ipAddr-application.name-port 可自定义修改
instanceId ,hostname,ipaddr 不建议设置,可采用默认
leaseRenewalIntervalInSeconds 默认是30秒,也就是每30秒会向Eureka Server发起Renew(续约)操做
基本流程
客户端
在com.netflix.discovery.DiscoveryClient启动的时候,将本地配置信息注册到注册中心,初始化定时任务,定时调用renew,定时刷新本地缓存(注册中心其余服务信息默认三十秒)
服务端
Eviction(失效服务剔除)用来按期(默认为每60秒)在Eureka Server检测失效的服务,检测标准就是超过必定时间没有Renew的服务。
默认失效时间为90秒,也就是若是有服务超过90秒没有向Eureka Server发起Renew请求的话,就会被当作失效服务剔除掉。
服务端有自我保护机制,会在心跳总量没法维持到某一个阈值时触发,触发的结果就是evict过时instance的任务再也不驱逐任何实例。这个阈值的单位是分钟,计算方式是当前全部保存的instance,按照每分钟应该提交2次心跳(30秒心跳),再乘以能够配置的最低能容纳的半分比;
自我保护机制的目的是为了所在服务发生严重的网络分区时,依旧可以提供可用性,但为何要根据心跳做为依据?经过上面的分析咱们知道eureka server集群自己是基于http的,没法维持一个持久的状态,在整个系统的网络通讯中,在client到server, peer到peer之间,心跳信息应该远大于其余信息的传输量。那虽然peer之间并不根据彼此的心跳(自身也是client)作什么逻辑判断,但其下的client的心跳复制数据自己也足够做为判断分区的依据了。而在一个稳定理想的集群中,心跳的信息绝大多数应该是其余peer复制过来的,若是达到必定阈值,更多的可能性不是server和client发生分区,而是peer和peer之间发生分区,但自己client并无真的down掉。因此才有这种自我保护的触发机制—— 更高的几率是client可用,该机制经过配置能够关闭 eureka.server.enableSelfPreservation=false
public void evict(long additionalLeaseMs) { logger.debug("Running the evict task"); if (!isLeaseExpirationEnabled()) { logger.debug("DS: lease expiration is currently disabled."); return; } // We collect first all expired items, to evict them in random order. For large eviction sets, // if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it, // the impact should be evenly distributed across all applications. List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>(); for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) { Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue(); if (leaseMap != null) { for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) { Lease<InstanceInfo> lease = leaseEntry.getValue(); if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) { expiredLeases.add(lease); } } } } --- 此处判断是否开启 isSelfPreservationModeEnable() @Override public boolean isLeaseExpirationEnabled() { if (!isSelfPreservationModeEnabled()) { // The self preservation mode is disabled, hence allowing the instances to expire. return true; } return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold; }
服务集群数据同步
PeerEurekaNodes 类中能够发现有个定时任务具体作什么updatePeerEurekaNodes(...) 更新 peer Eureka Nodes
try { updatePeerEurekaNodes(resolvePeerUrls()); Runnable peersUpdateTask = new Runnable() { @Override public void run() { try { updatePeerEurekaNodes(resolvePeerUrls()); } catch (Throwable e) { logger.error("Cannot update the replica Nodes", e); } } }; taskExecutor.scheduleWithFixedDelay( peersUpdateTask, serverConfig.getPeerEurekaNodesUpdateIntervalMs(), serverConfig.getPeerEurekaNodesUpdateIntervalMs(), TimeUnit.MILLISECONDS ); } catch (Exception e) { throw new IllegalStateException(e); }
源码github https://github.com/zhaoqilong3031/SpringCloud/tree/master/spring-cloud-zuul