1、Eureka中的核心概念
2、Spring RestTemplate詳解
3、代碼實戰服務與負載均衡的客戶端
4、項目源碼與參考資料下載
5、參考文章javascript
Eureka服務治理體系支持跨平臺,雖然咱們前文使用了Spring Boot來做爲服務提供者,可是對於其餘技術平臺只要支持Eureka通訊機制,同樣也是能夠做爲服務提供者,換句話說,服務提供者既能夠是Java寫的,也能夠是python寫的,也能夠是js寫的。這些服務提供者將本身註冊到Eureka上,供其它應用發現而後調用,這就是咱們的服務提供者,服務提供者主要有以下一些功能:php
1.1 服務註冊
服務提供者在啓動的時候會經過發送REST請求將本身註冊到Eureka Server上,同時還攜帶了自身服務的一些元數據信息。Eureka Server在接收到這個REST請求以後,將元數據信息存儲在一個雙層結構的Map集合中,第一層的key是服務名,第二層的key是具體服務的實例名,咱們在上篇文章最後展現出來的截圖中,你們也能夠看出一些端倪,以下:css
同時,咱們在服務註冊時,須要確認一下eureka.client.register-with-eureka=true配置是否正確,該值默認就爲true,表示啓動註冊操做,若是設置爲false則不會啓動註冊操做。好比第一天中eureka_server須要設置爲false,eureka_provider須要設置爲true.html
1.2 服務同步
再說服務同步以前,我先描述一個場景:首先我有兩個服務註冊中心,地址分別是http://localhost:1111和http://localhost:1112,而後我還有兩個服務提供者,地址分別是http://localhost:8080和http://localhost:8081,而後我將8080這個服務提供者註冊到1111這個註冊中心上去,將8081這個服務提供者註冊到1112這個註冊中心上去,此時我在服務消費者中若是隻向1111這個註冊中心去查找服務提供者,那麼是否是隻能獲取到8080這個服務而獲取不到8081這個服務?你們看下面一張圖:java
答案是服務消費者能夠獲取到兩個服務提供者提供的服務。雖然兩個服務提供者的信息分別被兩個服務註冊中心所維護,可是因爲服務註冊中心之間也互相註冊爲服務,當服務提供者發送請求到一個服務註冊中心時,它會將該請求轉發給集羣中相連的其餘註冊中心,從而實現註冊中心之間的服務同步,經過服務同步,兩個服務提供者的服務信息咱們就能夠經過任意一臺註冊中心來獲取到。因此在第一天學習中,咱們已經將服務提供者註冊到兩個服務註冊中心了,在下面代碼實戰中,咱們會將兩個服務提供者註冊到高可用服務註冊中心,以實現服務同步。python
1.3 服務續約
在註冊完服務以後,服務提供者會維護一個心跳來不停的告訴Eureka Server:「我還在運行」,以防止Eureka Server將該服務實例從服務列表中剔除,這個動做稱之爲服務續約,和服務續約相關的屬性有兩個,以下:web
eureka.instance.lease-expiration-duration-in-seconds=90 eureka.instance.lease-renewal-interval-in-seconds=30
第一個配置用來定義服務失效時間,默認爲90秒,第二個用來定義服務續約的間隔時間,默認爲30秒。spring
消費者主要是從服務註冊中心獲取服務列表,拿到服務提供者的列表以後,服務消費者就知道去哪裏調用它所須要的服務了,咱們從下面幾點來進一步瞭解下服務消費者。apache
2.1 獲取服務
當咱們啓動服務消費者的時候,它會發送一個REST請求給服務註冊中心來獲取服務註冊中心上面的服務提供者列表,而Eureka Server上則會維護一份只讀的服務清單來返回給客戶端,這個服務清單並非實時數據,而是一份緩存數據,默認30秒更新一次,若是想要修改清單更新的時間間隔,能夠經過eureka.client.registry-fetch-interval-seconds=30來修改,單位爲秒(注意這個修改是在eureka-server上來修改)。另外一方面,咱們的服務消費端要確保具備獲取服務提供者的能力,此時要確保eureka.client.fetch-registry=true這個配置爲true。緩存
2.2 服務調用
服務消費者從服務註冊中心拿到服務提供者列表以後,經過服務名就能夠獲取具體提供服務的實例名和該實例的元數據信息,客戶端將根據這些信息來決定調用哪一個實例,咱們以前採用了Ribbon,Ribbon中默認採用輪詢的方式去調用服務提供者,進而實現了客戶端的負載均衡。
2.3 服務下線
服務提供者在運行的過程當中可能會發生關閉或者重啓,當服務進行正常關閉時,它會觸發一個服務下線的REST請求給Eureka Server,告訴服務註冊中心我要下線了,服務註冊中心收到請求以後,將該服務狀態置爲DOWN,表示服務已下線,並將該事件傳播出去,這樣就能夠避免服務消費者調用了一個已經下線的服務提供者了。
2.4 服務註冊中心
服務註冊中心就是Eureka提供的服務端,它提供了服務註冊與發現功能。
2.5 失效剔除
上面咱們說到了服務下線問題,正常的服務下線發生流程有一個前提那就是服務正常關閉,可是在實際運行中服務有可能沒有正常關閉,好比系統故障、網絡故障等緣由致使服務提供者非正常下線,那麼這個時候對於已經下線的服務Eureka採用了定時清除:Eureka Server在啓動的時候會建立一個定時任務,每隔60秒就去將當前服務提供者列表中超過90秒還沒續約的服務剔除出去,經過這種方式來避免服務消費者調用了一個無效的服務。
2.6 自我保護
咱們在實際操做中,可能會遇到一個這樣的警告,以下圖:
這個警告實際上就是觸發了Eureka Server的自我保護機制。Eureka Server在運行期間會去統計心跳失敗比例在15分鐘以內是否低於85%,若是低於85%,Eureka Server會將這些實例保護起來,讓這些實例不會過時,可是在保護期內若是服務恰好這個服務提供者非正常下線了,此時服務消費者就會拿到一個無效的服務實例,此時會調用失敗,對於這個問題須要服務消費者端要有一些容錯機制,如重試,斷路器等。咱們在單機測試的時候很容易知足心跳失敗比例在15分鐘以內低於85%,這個時候就會觸發Eureka的保護機制,一旦開啓了保護機制,則服務註冊中心維護的服務實例就不是那麼準確了,此時咱們可使用eureka.server.enable-self-preservation=false來關閉保護機制,這樣能夠確保註冊中心中不可用的實例被及時的剔除。
3.1 服務端負載均衡
負載均衡是咱們處理高併發、緩解網絡壓力和進行服務端擴容的重要手段之一,可是通常狀況下咱們所說的負載均衡一般都是指服務端負載均衡,服務端負載均衡又分爲兩種,一種是硬件負載均衡,還有一種是軟件負載均衡。
硬件負載均衡主要經過在服務器節點之間安裝專門用於負載均衡的設備,常見的如F5。
軟件負載均衡則主要是在服務器上安裝一些具備負載均衡功能的軟件來完成請求分發進而實現負載均衡,常見的就是Nginx。
不管是硬件負載均衡仍是軟件負載均衡,它的工做原理都不外乎下面這張圖:
3.2 客戶端負載均衡
咱們在Spring Cloud中服務的發現與消費一文中涉及到了客戶端負載均衡,在那篇文章中咱們提到
「Ribbo是一個基於HTTP和TCP的客戶端負載均衡器,當咱們將Ribbon和Eureka一塊兒使用時,Ribbon會從Eureka註冊中心去獲取服務端列表,而後進行輪詢訪問以到達負載均衡的做用,客戶端負載均衡中也須要心跳機制去維護服務端清單的有效性,固然這個過程須要配合服務註冊中心一塊兒完成。」
從上面的描述咱們能夠看出,客戶端負載均衡和服務端負載均衡最大的區別在於服務清單所存儲的位置。在客戶端負載均衡中,全部的客戶端節點都有一份本身要訪問的服務端清單,這些清單通通都是從Eureka服務註冊中心獲取的。在Spring Cloud中咱們若是想要使用客戶端負載均衡,方法很簡單,開啓@LoadBalanced
註解便可,這樣客戶端在發起請求的時候會先自行選擇一個服務端,向該服務端發起請求,從而實現負載均衡。具體小夥伴們能夠參考Spring Cloud中服務的發現與消費這篇文章。
4.1 IRule
這是全部負載均衡策略的父接口,裏邊的核心方法就是choose方法,用來選擇一個服務實例。
4.2 AbstractLoadBalancerRule
AbstractLoadBalancerRule是一個抽象類,裏邊主要定義了一個ILoadBalancer,就是咱們上文所說的負載均衡器,負載均衡器的功能咱們在上文已經說的很詳細了,這裏就再也不贅述,這裏定義它的目的主要是輔助負責均衡策略選取合適的服務端實例。
4.3 RandomRule
看名字就知道,這種負載均衡策略就是隨機選擇一個服務實例,看源碼咱們知道,在RandomRule的無參構造方法中初始化了一個Random對象,而後在它重寫的choose方法又調用了choose(ILoadBalancer lb, Object key)這個重載的choose方法,在這個重載的choose方法中,每次利用random對象生成一個不大於服務實例總數的隨機數,並將該數做爲下標因此獲取一個服務實例。
4.4 RoundRobinRule
RoundRobinRule這種負載均衡策略叫作線性負載均衡策略,也就是咱們在上文所說的BaseLoadBalancer負載均衡器中默認採用的負載均衡策略。這個類的choose(ILoadBalancer lb, Object key)函數總體邏輯是這樣的:開啓一個計數器count,在while循環中遍歷服務清單,獲取清單以前先經過incrementAndGetModulo方法獲取一個下標,這個下標是一個不斷自增加的數先加1而後和服務清單總數取模以後獲取到的(因此這個下標歷來不會越界),拿着下標再去服務清單列表中取服務,每次循環計數器都會加1,若是連續10次都沒有取到服務,則會報一個警告No available alive servers after 10 tries from load balancer: XXXX。
4.5 RetryRule
看名字就知道這種負載均衡策略帶有重試功能。首先RetryRule中又定義了一個subRule,它的實現類是RoundRobinRule,而後在RetryRule的choose(ILoadBalancer lb, Object key)方法中,每次仍是採用RoundRobinRule中的choose規則來選擇一個服務實例,若是選到的實例正常就返回,若是選擇的服務實例爲null或者已經失效,則在失效時間deadline以前不斷的進行重試(重試時獲取服務的策略仍是RoundRobinRule中定義的策略),若是超過了deadline仍是沒取到則會返回一個null。
4.6 WeightedResponseTimeRule
WeightedResponseTimeRule是RoundRobinRule的一個子類,在WeightedResponseTimeRule中對RoundRobinRule的功能進行了擴展,WeightedResponseTimeRule中會根據每個實例的運行狀況來給計算出該實例的一個權重,而後在挑選實例的時候則根據權重進行挑選,這樣可以實現更優的實例調用。WeightedResponseTimeRule中有一個名叫DynamicServerWeightTask的定時任務,默認狀況下每隔30秒會計算一次各個服務實例的權重,權重的計算規則也很簡單,若是一個服務的平均響應時間越短則權重越大,那麼該服務實例被選中執行任務的機率也就越大。
4.7 ClientConfigEnabledRoundRobinRule
ClientConfigEnabledRoundRobinRule選擇策略的實現很簡單,內部定義了RoundRobinRule,choose方法仍是採用了RoundRobinRule的choose方法,因此它的選擇策略和RoundRobinRule的選擇策略一致,不贅述。
4.8 BestAvailableRule
BestAvailableRule繼承自ClientConfigEnabledRoundRobinRule,它在ClientConfigEnabledRoundRobinRule的基礎上主要增長了根據loadBalancerStats中保存的服務實例的狀態信息來過濾掉失效的服務實例的功能,而後順便找出併發請求最小的服務實例來使用。然而loadBalancerStats有可能爲null,若是loadBalancerStats爲null,則BestAvailableRule將採用它的父類即ClientConfigEnabledRoundRobinRule的服務選取策略(線性輪詢)。
4.9 PredicateBasedRule
PredicateBasedRule是ClientConfigEnabledRoundRobinRule的一個子類,它先經過內部定義的一個過濾器過濾出一部分服務實例清單,而後再採用線性輪詢的方式從過濾出來的結果中選取一個服務實例。
4.10 ZoneAvoidanceRule
ZoneAvoidanceRule是PredicateBasedRule的一個實現類,只不過這裏多一個過濾條件,ZoneAvoidanceRule中的過濾條件是以ZoneAvoidancePredicate爲主過濾條件和以AvailabilityPredicate爲次過濾條件組成的一個叫作CompositePredicate的組合過濾條件,過濾成功以後,繼續採用線性輪詢的方式從過濾結果中選擇一個出來。
當咱們從服務消費端去調用服務提供者的服務的時候,使用了一個很好用的對象,叫作RestTemplate,咱們只使用了RestTemplate中最簡單的一個功能getForEntity發起了一個get請求去調用服務端的數據,同時,咱們還經過配置@LoadBalanced註解開啓客戶端負載均衡,RestTemplate的功能不可謂不強大,那麼今天咱們就來詳細的看一下RestTemplate中幾種常見請求方法的使用。
在RestTemplate中,發送一個GET請求,咱們能夠經過以下兩種方式:
2.1 getForEntity
getForEntity方法的返回值是一個ResponseEntity<T>,ResponseEntity<T>是Spring對HTTP請求響應的封裝,包括了幾個重要的元素,如響應碼、contentType、contentLength、響應消息體等。好比下面一個例子:
@RequestMapping("/gethello") public String getHello() { ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class); String body = responseEntity.getBody(); HttpStatus statusCode = responseEntity.getStatusCode(); int statusCodeValue = responseEntity.getStatusCodeValue(); HttpHeaders headers = responseEntity.getHeaders(); StringBuffer result = new StringBuffer(); result.append("responseEntity.getBody():").append(body).append("<hr>") .append("responseEntity.getStatusCode():").append(statusCode).append("<hr>") .append("responseEntity.getStatusCodeValue():").append(statusCodeValue).append("<hr>") .append("responseEntity.getHeaders():").append(headers).append("<hr>"); return result.toString(); }
關於這段代碼,我說以下幾點:
getForEntity的第一個參數爲我要調用的服務的地址,這裏我調用了服務提供者提供的/hello接口,注意這裏是經過服務名調用而不是服務地址,若是寫成服務地址就無法實現客戶端負載均衡了。
getForEntity第二個參數String.class表示我但願返回的body類型是String
拿到返回結果以後,將返回結果遍歷打印出來,最終顯示結果以下:
有時候我在調用服務提供者提供的接口時,可能須要傳遞參數,有兩種不一樣的方式,以下:
@RequestMapping("/sayhello") public String sayHello() { ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={1}", String.class, "張三"); return responseEntity.getBody(); } @RequestMapping("/sayhello2") public String sayHello2() { Map<String, String> map = new HashMap<>(); map.put("name", "李四"); ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={name}", String.class, map); return responseEntity.getBody(); }
能夠用一個數字作佔位符,最後是一個可變長度的參數,來一一替換前面的佔位符
也能夠前面使用name={name}這種形式,最後一個參數是一個map,map的key即爲前邊佔位符的名字,map的value爲參數值
第一個調用地址也能夠是一個URI而不是字符串,這個時候咱們構建一個URI便可,參數神馬的都包含在URI中了,以下:
@RequestMapping("/sayhello3") public String sayHello3() { UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/sayhello?name={name}").build().expand("王五").encode(); URI uri = uriComponents.toUri(); ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class); return responseEntity.getBody(); }
經過Spring中提供的UriComponents來構建Uri便可。
固然,服務提供者不只能夠返回String,也能夠返回一個自定義類型的對象,好比個人服務提供者中有以下方法:
@RequestMapping(value = "/getbook1", method = RequestMethod.GET) public Book book1() { return new Book("三國演義", 90, "羅貫中", "花城出版社"); }
對於該方法我能夠在服務消費者中經過以下方式來調用:
@RequestMapping("/book1") public Book book1() { ResponseEntity<Book> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/getbook1", Book.class); return responseEntity.getBody(); }
運行結果以下:
2.2 getForObject
getForObject函數其實是對getForEntity函數的進一步封裝,若是你只關注返回的消息體的內容,對其餘信息都不關注,此時可使用getForObject,舉一個簡單的例子,以下:
@RequestMapping("/book2") public Book book2() { Book book = restTemplate.getForObject("http://HELLO-SERVICE/getbook1", Book.class); return book; }
getForObject也有幾個重載方法,以下:
這幾個重載方法參數的含義和getForEntity一致,我就再也不贅述了。
在RestTemplate中,POST請求能夠經過以下三個方法來發起:
3.1 postForEntity
該方法和get請求中的getForEntity方法相似,以下例子:
@RequestMapping("/book3") public Book book3() { Book book = new Book(); book.setName("紅樓夢"); ResponseEntity<Book> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/getbook2", book, Book.class); return responseEntity.getBody(); }
方法的第一參數表示要調用的服務的地址
方法的第二個參數表示上傳的參數
方法的第三個參數表示返回的消息體的數據類型
我這裏建立了一個Book對象,這個Book對象只有name屬性有值,將之傳遞到服務提供者那裏去,服務提供者代碼以下:
@RequestMapping(value = "/getbook2", method = RequestMethod.POST) public Book book2(@RequestBody Book book) { System.out.println(book.getName()); book.setPrice(33); book.setAuthor("曹雪芹"); book.setPublisher("人民文學出版社"); return book; }
服務提供者接收到服務消費者傳來的參數book,給其餘屬性設置上值再返回,調用結果以下:
postForEntity的其餘重載方法以下:
這些方法的參數含義和getForEntity參數的含義一致,再也不贅述。
3.2 postForObject
若是你只關注,返回的消息體,能夠直接使用postForObject。用法和getForObject一致。
3.3 postForLocation
postForLocation也是提交新資源,提交成功以後,返回新資源的URI,postForLocation的參數和前面兩種的參數基本一致,只不過該方法的返回值爲Uri,這個只須要服務提供者返回一個Uri便可,該Uri表示新資源的位置。
在RestTemplate中,PUT請求能夠經過put方法調用,put方法的參數和前面介紹的postForEntity方法的參數基本一致,只是put方法沒有返回值而已。舉一個簡單的例子,以下:
@RequestMapping("/put") public void put() { Book book = new Book(); book.setName("紅樓夢"); restTemplate.put("http://HELLO-SERVICE/getbook3/{1}", book, 99); }
book對象是我要提交的參數,最後的99用來替換前面的佔位符{1}
delete請求咱們能夠經過delete方法調用來實現,以下例子:
@RequestMapping("/delete") public void delete() { restTemplate.delete("http://HELLO-SERVICE/getbook4/{1}", 100); }
delete方法也有幾個重載的方法,不太重載的參數和前面基本一致,不贅述。
https://mp.weixin.qq.com/s/uvJDmN2f9y3EEI6A3ss_aQ
相信小夥伴們已經能夠本身搭建一個單節點或者多節點的服務註冊中心了,同時也可以向這個服務註冊中心去註冊服務。服務註冊成功了,咱們就該發現和消費服務了,今天咱們就來看看如何實現服務的發現與消費
1.1 啓動服務註冊中心
1.2 啓動服務提供者
2.1 建立Spring Boot項目
建立名爲ribbon-consumer的springboot項目
建立後項目結構以下:
2.2 pom.xml添加配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.sang</groupId> <artifactId>ribbon-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ribbon-consumer</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
這裏重點是添加了spring-cloud-starter-eureka和spring-cloud-starter-ribbon依賴。
2.3 資源文件配置
建立後的資源文件結構以下:
其中static、templates的文件夾內容爲空,application.properties文件內容以下:
server.port=9000 spring.application.name=ribbon-consumer eureka.client.service-url.defaultZone=http://peer1:1111/eureka,http://peer2:1112/eureka
注意服務消費者的端口號不要和前面的端口號衝突。
2.4 建立Controller進行服務消費
在com.wxc.test包下新建HelloController.java,並向Controller類中注入RestTemplate對象,同時在Controller中提供一個名爲/ribbon-consumer
的接口,在該接口中,咱們經過剛剛注入的restTemplate來實現對HELLO-SERVICE服務提供的/hello接口進行調用(上篇文章中咱們有詳細介紹HELLO-SERVICE的實現)。在調用的過程當中,咱們的訪問地址是HELLO-SERVICE,而不是一個具體的地址。OK,基於以上理解,咱們的Controller以下:
@RestController public class ConsumerController { @Autowired RestTemplate restTemplate; @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET) public String helloController() { return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody(); } }
2.5 建立項目啓動類
入口類上咱們須要作兩件事:
亮明Eureka客戶端身份
首先在入口類上添加@EnableDiscoveryClient註解,表示該應用是一個Eureka客戶端應用,這樣該應用就自動具有了發現服務的能力。
提供RestTemplate的Bean
RestTemplate能夠幫助咱們發起一個GET或者POST請求,這個咱們在後文會詳細解釋,這裏咱們只須要提供一個RestTemplate Bean就能夠了,在提供Bean的同時,添加@LoadBalanced註解,表示開啓客戶端負載均衡。
OK,基於以上兩點,咱們的入口類以下:
@SpringBootApplication @EnableDiscoveryClient public class RibbonConsumerApplication { public static void main(String[] args) { SpringApplication.run(RibbonConsumerApplication.class, args); } @LoadBalanced @Bean RestTemplate restTemplate() { return new RestTemplate(); } }
2.6 啓動項目並訪問
項目啓動
啓動成功
項目訪問
咱們向localhost:9000/ribbon-consumer地址發起請求,就能夠看到provider工程中/hello接口返回的Hello World,以下:
那麼這個時候有小夥伴可能會有疑問,這個Hello World是由哪個provider提供的?由於咱們在前面啓動了兩個provider實例。咱們看下面一張圖:
小夥伴們看到,上面那個日誌是由端口號爲8081的provider打印出來的,下面的日誌是由端口號爲8080的provider打印出來的,說明咱們這裏的負載均衡已經起做用了。此時咱們觀察客戶端的日誌,以下:
小夥伴們看到Ribbon輸出了當前客戶端維護的HELLO-SERVICE的服務列表狀況,每個provider的位置都展現出來,Ribbon就是按照這個列表進行輪詢,進而實現基於客戶端的負載均衡。同時這裏的日誌還輸出了其餘信息,好比各個實例的請求總數量,第一次鏈接信息,上一次鏈接信息以及總的請求失敗數量等。
連接:https://pan.baidu.com/s/1D7zZBq422jhbRWFk7lmMIQ
提取碼:qcyl