CAP知足哪幾部分html
failover方式是怎樣java
語言機制node
DNS
最原始的配置文件和 DNS 來作服務發現,Host、端口都是寫在配置文件裏的,發生變動的時候只能修改配置文件並重啓服務。因此當某臺機器掛掉的時候,依賴它上面服務的其餘系統也都所有會出問題。而應急的步驟都是先在別的機器上運行新的實例,修改配置文件並重啓關聯的其餘系統。這樣作費時、費力、且會有一個時間窗口內系統沒法提供服務。git
經過 Nginx 來作了負載均衡/主備的
這樣作仍是有兩個問題:(1)Nginx 自己成爲一個故障點(2)鏈接數量翻倍,其中第二個問題曾致使咱們的環境出現了 nf_conntrack table full 的問題。咱們的關鍵服務都是多實例負載均衡的,當系統併發上升到必定程度的時候,某些服務器,尤爲是跑着 Nginx 的機器很容易出現這個錯誤。github
zk
服務實例註冊的 Node 類型是 ephemeral node,這種類型的節點只有在客戶端保持着鏈接的時候纔有效。因此當某個服務實例被中止或者出現網絡異常的時候,對應的節點也會被刪掉。所以,任什麼時候候從 ZooKeeper 裏查詢到的都是當前活躍的實例。藉助 ZooKeeper 的推送功能,服務的消費者能夠得知實例的變化,從而能夠從容應對服務實例的宕機和新實例的添加,無需重啓。spring
SmartStack: Airbnb的自動服務發現和註冊框架後端
DNS 變動延遲問題緩存
中心化負載均衡,單點問題安全
zk,多語言問題服務器
SmartStack,在zookeeper和haproxy上封裝一層
etcd(coreos開發,系統級別的
)
etcd是一個採用HTTP協議的健/值對存儲系統,它是一個分佈式和功能層次配置系統,可用於構建服務發現系統。其很容易部署、安裝和使用,提供了可靠的數據持久化特性。它是安全的而且文檔也十分齊全。etcd比Zookeeper是比更好的選擇,由於它很簡單,然而,它須要搭配一些第三方工具才能夠提供服務發現功能。
consul(go語言寫的
)
Consul是強一致性的數據存儲,使用gossip造成動態集羣。它提供分級鍵/值存儲方式,不只能夠存儲數據,並且能夠用於註冊器件事各類任務,從發送數據改變通知到運行健康檢查和自定義命令,具體如何取決於它們的輸出。與Zookeeper和etcd不同,Consul內嵌實現了服務發現系統,因此這樣就不須要構建本身的系統或使用第三方系統。這一發現系統除了上述提到的特性以外,還包括節點健康檢查和運行在其上的服務。Zookeeper和etcd只提供原始的鍵/值隊存儲,要求應用程序開發人員構建他們本身的系統提供服務發現功能。而Consul提供了一個內置的服務發現的框架。客戶只須要註冊服務並經過DNS或HTTP接口執行服務發現。其餘兩個工具須要一個親手製做的解決方案或藉助於第三方工具。Consul爲多種數據中心提供了開箱即用的原生支持,其中的gossip系統不只能夠工做在同一集羣內部的各個節點,並且還能夠跨數據中心工做。
spring-cloud-netflix文檔(裏頭有peer部署部分
)
Eureka 由兩個組件組成: Eureka 服務器 和 Eureka 客戶端 。Eureka 服務器用做服務註冊服務器。Eureka 客戶端是一個 java 客戶端,用來簡化與服務器的交互、做爲輪詢負載均衡器,並提供服務的故障切換支持。Netflix 在其生產環境中使用的是另外的客戶端,它提供基於流量、資源利用率以及出錯狀態的加權負載均衡。
當一箇中間層服務首次啓動時,他會將本身註冊到 Eureka 中,以便讓客戶端找到它,同時每 30 秒發送一次心跳。若是一個服務在幾分鐘內沒有發送心跳,它將從全部 Eureka 節點上註銷。一個 Amazon 域中能夠有一個 Eureka 節點集羣,每一個可用區(Availability Zone)至少有一個 Eureka 節點。AWS 的域相互之間是隔離的。
zk是知足CP犧牲A,這個不對,看ZooKeeper和CAP理論及一致性原則 ,其實zk只是知足最終一致性C,可用性A這個是保證的,而且保證一半的節點是最新的數據,分區性P這個得看節點多少及讀寫狀況,節點多,則寫耗時長,另外節點多了Leader選舉很是耗時, 就會放大網絡的問題,容易分區。
對於Service發現服務而言,寧肯返回某服務5分鐘以前在哪幾個服務器上可用的信息,也不能由於暫時的網絡故障而找不到可用的服務器,而不返回任何結果。因此說,用ZooKeeper來作Service發現服務是確定錯誤的。總結一句就是,service不是強一致的,因此會有部分狀況下沒發現新服務致使請求出錯。當部分或者全部節點跟ZooKeeper斷開的狀況下,每一個節點還能夠從本地緩存中獲取到數據;可是,即使如此,ZooKeeper下全部節點不可能保證任什麼時候候都能緩存全部的服務註冊信息。若是ZooKeeper下全部節點都斷開了,或者集羣中出現了網絡分割的故障(注:因爲交換機故障致使交換機底下的子網間不能互訪);那麼ZooKeeper會將它們都從本身管理範圍中剔除出去,外界就不能訪問到這些節點了,即使這些節點自己是「健康」的,能夠正常提供服務的;因此致使到達這些節點的服務請求被丟失了。
Eureka處理網絡問題致使分區。若是Eureka服務節點在短期裏丟失了大量的心跳鏈接(注:可能發生了網絡故障),那麼這個Eureka節點會進入」自我保護模式「,同時保留那些「心跳死亡「的服務註冊信息不過時。此時,這個Eureka節點對於新的服務還能提供註冊服務,對於」死亡「的仍然保留,以防還有客戶端向其發起請求。當網絡故障恢復後,這個Eureka節點會退出」自我保護模式「。因此Eureka的哲學是,同時保留」好數據「與」壞數據「總比丟掉任何」好數據「要更好,因此這種模式在實踐中很是有效。
Eureka就是爲發現服務所設計的,它有獨立的客戶端程序庫,同時提供心跳服務、服務健康監測、自動發佈服務與自動刷新緩存的功能。可是,若是使用ZooKeeper你必須本身來實現這些功能。
Eureka: How do I disable/configure peer replication?
經過配置eureka.serviceUrl.defaultZone來進行復制eureka.client.serviceUrl.defaultZone=http://<peer1host>:<peer1port&g...
How to config multiple Eureka Servers from client in Spring Cloud
貌似是根據配置的url的先後順序來複制的
/** * Replicates all eureka actions to peer eureka nodes except for replication * traffic to this node. * */ private void replicateToPeers(Action action, String appName, String id, InstanceInfo info /* optional */, InstanceStatus newStatus /* optional */, boolean isReplication) { Stopwatch tracer = action.getTimer().start(); try { if (isReplication) { numberOfReplicationsLastMin.increment(); } // If it is a replication already, do not replicate again as this // will create a poison replication if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) { return; } for (final PeerEurekaNode node : peerEurekaNodes.get()) { // If the url represents this host, do not replicate // to yourself. if (isThisMe(node.getServiceUrl())) { continue; } replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node); } } finally { tracer.stop(); } }
觸發的時機至關於熱備:每增刪改一次,就同步一次。而後默認是走url的第一個來查的,而後
調用的時候,第一個掛了,自動去找第二,詳見
/** * Makes remote calls with the corresponding action(register,renew etc). * * @param action * the action to be performed on eureka server. * @return ClientResponse the HTTP response object. * @throws Throwable * on any error. */ private ClientResponse makeRemoteCall(Action action) throws Throwable { return makeRemoteCall(action, 0); }
具體內部catch異常後,遞歸調用
/** * Makes remote calls with the corresponding action(register,renew etc). * * @param action * the action to be performed on eureka server. * * Try the fallback servers in case of problems communicating to * the primary one. * * @return ClientResponse the HTTP response object. * @throws Throwable * on any error. */ private ClientResponse makeRemoteCall(Action action, int serviceUrlIndex) throws Throwable { String urlPath = null; Stopwatch tracer = null; String serviceUrl = eurekaServiceUrls.get().get(serviceUrlIndex); ClientResponse response = null; logger.debug("Discovery Client talking to the server {}", serviceUrl); try { // If the application is unknown do not register/renew/cancel but // refresh if ((UNKNOWN.equals(instanceInfo.getAppName()) && (!Action.Refresh.equals(action)) && (!Action.Refresh_Delta .equals(action)))) { return null; } WebResource r = discoveryApacheClient.resource(serviceUrl); String remoteRegionsToFetchStr; switch (action) { case Renew: tracer = RENEW_TIMER.start(); urlPath = "apps/" + appPathIdentifier; response = r .path(urlPath) .queryParam("status", instanceInfo.getStatus().toString()) .queryParam("lastDirtyTimestamp", instanceInfo.getLastDirtyTimestamp().toString()) .put(ClientResponse.class); break; case Refresh: tracer = REFRESH_TIMER.start(); final String vipAddress = clientConfig.getRegistryRefreshSingleVipAddress(); urlPath = vipAddress == null ? "apps/" : "vips/" + vipAddress; remoteRegionsToFetchStr = remoteRegionsToFetch.get(); if (!Strings.isNullOrEmpty(remoteRegionsToFetchStr)) { urlPath += "?regions=" + remoteRegionsToFetchStr; } response = getUrl(serviceUrl + urlPath); break; case Refresh_Delta: tracer = REFRESH_DELTA_TIMER.start(); urlPath = "apps/delta"; remoteRegionsToFetchStr = remoteRegionsToFetch.get(); if (!Strings.isNullOrEmpty(remoteRegionsToFetchStr)) { urlPath += "?regions=" + remoteRegionsToFetchStr; } response = getUrl(serviceUrl + urlPath); break; case Register: tracer = REGISTER_TIMER.start(); urlPath = "apps/" + instanceInfo.getAppName(); response = r.path(urlPath) .type(MediaType.APPLICATION_JSON_TYPE) .post(ClientResponse.class, instanceInfo); break; case Cancel: tracer = CANCEL_TIMER.start(); urlPath = "apps/" + appPathIdentifier; response = r.path(urlPath).delete(ClientResponse.class); // Return without during de-registration if it is not registered // already and if we get a 404 if ((!isRegisteredWithDiscovery) && (response.getStatus() == Status.NOT_FOUND .getStatusCode())) { return response; } break; } if (logger.isDebugEnabled()) { logger.debug("Finished a call to service url {} and url path {} with status code {}.", new String[] {serviceUrl, urlPath, String.valueOf(response.getStatus())}); } if (isOk(action, response.getStatus())) { return response; } else { logger.warn("Action: " + action + " => returned status of " + response.getStatus() + " from " + serviceUrl + urlPath); throw new RuntimeException("Bad status: " + response.getStatus()); } } catch (Throwable t) { closeResponse(response); String msg = "Can't get a response from " + serviceUrl + urlPath; if (eurekaServiceUrls.get().size() > (++serviceUrlIndex)) { logger.warn(msg, t); logger.warn("Trying backup: " + eurekaServiceUrls.get().get(serviceUrlIndex)); SERVER_RETRY_COUNTER.increment(); return makeRemoteCall(action, serviceUrlIndex); } else { ALL_SERVER_FAILURE_COUNT.increment(); logger.error( msg + "\nCan't contact any eureka nodes - possibly a security group issue?", t); throw t; } } finally { if (tracer != null) { tracer.stop(); } } }
consul與其餘方案的對比(zk,etcd,samrtstack
)
Building 「Bootiful」 Microservices with Spring Cloud by Josh Long