服務註冊與發現之Eureka

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

相關文章
相關標籤/搜索