服務註冊發現-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