SpringCloud(三)Ribbon與Feign

来源:https://www.cnblogs.com/yuanqinnan/archive/2019/09/16/11530737.html
-Advertisement-
Play Games

上一篇使用了Eureka與Ribbon組件做了最簡單的的服務註冊與發現,我們知道Eureka是實現服務治理中心的組件,但是上一篇Eureka沒有實現集群,這樣沒有保證到Eureka Server的高可用。 理論上來講,因為服務消費者本地緩存了服務提供者的地址,即使Eureka Server宕機,也不 ...


上一篇使用了Eureka與Ribbon組件做了最簡單的的服務註冊與發現,我們知道Eureka是實現服務治理中心的組件,但是上一篇Eureka沒有實現集群,這樣沒有保證到Eureka Server的高可用。

理論上來講,因為服務消費者本地緩存了服務提供者的地址,即使Eureka Server宕機,也不會影響服務之間的調用,但是一旦新服務上線,已經在緩存在本地的服務提供者不可用了,服務消費者也無法知道,所以保證Eureka Server的高可用還是很有必要的。

那我們先來搭建一個Eureka的集群

一、Eureka集群配置

要做集群,我們想到肯定是增加一臺伺服器,那怎麼讓伺服器之間產生關係,先讓他們相互註冊,在修改之前,我們為了區分伺服器名稱,先修改下hosts文件,增加下麵一段:

127.0.0.1  eureka1.com 
127.0.0.1  eureka2.com

然後修改下之前spring-cloud-learn-eureka 項目的配置文件,主要修改的是註冊的地址:

spring:
  application:
    name: spring-cloud-learn-eureka

server:
  port: 8761
eureka:
  instance:
    hostname: eureka1.com
  client:
    #表示是否將自己註冊到Eureka Server,預設為true。
    registerWithEureka: false
    #表示是否從Eureka Server獲取註冊信息,預設為true。
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://eureka2.com:8762/eureka/

這個時候我們啟動此項目,並同時啟動服務提供者spring-cloud-learn-provider-dept,這裡其實跟之前都沒什麼區別,服務順利的註冊進去:

這個時候我們修改spring-cloud-learn-eureka的配置,即再啟動一個eureka server :

spring:
  application:
    name: spring-cloud-learn-eureka

server:
  port: 8762
eureka:
  instance:
    hostname: eureka2.com
  client:
    #表示是否將自己註冊到Eureka Server,預設為true。
    registerWithEureka: false
    #表示是否從Eureka Server獲取註冊信息,預設為true。
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://eureka1.com:8761/eureka/

這是啟動的第二台伺服器,註冊到了第一臺伺服器中,啟動項目,這個時候我們訪問:http://eureka2.com:8762/

 這個時候我們發現,第二台服務也註冊進來了,是因為這兩個Eureka伺服器互相同步信息,這樣就已經完成集群了。

接著實現,這個時候我們停掉eureka1服務,那eureka2中依然有服務,這說明掛掉一臺伺服器註冊服務仍然可用,那我們在想想,如果我們重啟服務提供者spring-cloud-learn-provider-dept會發生什麼呢?

啟動的時候,我們可以看到控制台是會報錯誤的,因為在spring-cloud-learn-provider-dept我們只向eureka1進行了註冊,那此時eureka1停掉之後找不到註冊地址,就會報錯,但是只是刷新http://eureka2.com:8762/,我們發現此時服務提供者依然存在,這個是因為的自我保護機制。

如果我們重啟eureka2服務,那這個時候就會發現此時spring-cloud-learn-provider-dept 就沒有了,這個也很好理解,在啟動時兩個服務會互相同步消息,而這個時候服務提供者未註冊到eureka1,那eureka2啟動也是沒有效果的,為瞭解決這個問題,那就讓服務提供者分別向兩台eureka伺服器進行註冊:

spring:
  application:
    name: spring-cloud-learn-provider-dept

server:
  port: 8763

eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka1.com:8761/eureka/,http://eureka2.com:8762/eureka/

二、Eureka與ZooKeeper的比較

Eureka與ZooKeeper的主要區別在CAP原則的選擇上,CAP原則是指的是在一個分散式系統中,Consistency(一致性)Availability(可用性)Partition tolerance(分區容錯性),三者不可兼得(我們常說的魚和熊掌不可兼得)。CAP 原則也是 NoSQL 資料庫的基石。

  • 一致性(Consistency,C):在分散式系統中的所有數據備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的數據副本)。

  • 可用性(Availability,A):在一個分散式系統的集群中一部分節點故障後,該集群是否還能夠正常響應客戶端的讀寫請求。(對數據更新具備高可用性)。

  • 分區容錯性(Partition tolerance,P):大多數的分散式系統都分佈在多個子網路中,而每個子網路就叫做一個區(partition)。分區容錯的意思是,區間通信可能失敗。在一個分散式系統中一般分區容錯是無法避免的,因此可以認為 CAP 中的 P 總是成立的。CAP 理論告訴我們,在 C 和 A 之間是無法同時做到。

這個時候Eureka選擇了 AP,在剛剛的集群配置中,Eureka Server 採用的是Peer to Peer 對等通信。這是一種去中心化的架構(參看:微服務與微服務架構思想與原則),無 master/slave 之分,每一個 Peer 都是對等的。在這種架構風格中,節點通過彼此互相註冊來提高可用性,每個節點需要添加一個或多個有效的 serviceUrl 指向其他節點。每個節點都可被視為其他節點的副本。

在集群環境中如果某台 Eureka Server 宕機,Eureka Client 的請求會自動切換到新的 Eureka Server 節點上,當宕機的伺服器重新恢復後,Eureka 會再次將其納入到伺服器集群管理之中。當節點開始接受客戶端請求時,所有的操作都會在節點間進行複製(replicate To Peer)操作,將請求複製到該 Eureka Server 當前所知的其它所有節點中。

當一個新的 Eureka Server 節點啟動後,會首先嘗試從鄰近節點獲取所有註冊列表信息,並完成初始化。Eureka Server 通過 getEurekaServiceUrls() 方法獲取所有的節點,並且會通過心跳契約的方式定期更新。預設情況下,如果 Eureka Server 在一定時間內沒有接收到某個服務實例的心跳(預設周期為30秒),Eureka Server 將會註銷該實例(預設為90秒,如果某個 eureka.instance.lease-expiration-duration-in-seconds 進行自定義配置)。當 Eureka Server 節點在短時間內丟失過多的心跳時,那麼這個節點就會進入自我保護模式。

與 Eureka 有所不同,Zookeeper 在設計時就緊遵CP原則,即任何時候對 Zookeeper 的訪問請求能得到一致的數據結果,同時系統對網路分割具備容錯性,但是 Zookeeper 不能保證每次服務請求都是可達的。從 Zookeeper 的實際應用情況來看,在使用 Zookeeper 獲取服務列表時,如果此時的 Zookeeper 集群中的 Leader 宕機了,該集群就要進行 Leader 的選舉,又或者 Zookeeper 集群中半數以上伺服器節點不可用(例如有三個節點,如果節點一檢測到節點三掛了 ,節點二也檢測到節點三掛了,那這個節點才算是真的掛了),那麼將無法處理該請求。所以說,Zookeeper 不能保證服務可用性。

當然,在大多數分散式環境中,尤其是涉及到數據存儲的場景,數據一致性應該是首先被保證的,這也是 Zookeeper 設計緊遵CP原則的另一個原因。但是對於服務發現來說,情況就不太一樣了,針對同一個服務,即使註冊中心的不同節點保存的服務提供者信息不盡相同,也並不會造成災難性的後果。因為對於服務消費者來說,能消費才是最重要的,消費者雖然拿到可能不正確的服務實例信息後嘗試消費一下,也要勝過因為無法獲取實例信息而不去消費,導致系統異常要好。

三、Ribbon介紹

Ribbon是負責客戶端負載均衡的工具,與Nginx的作用類似,負載均衡應該大部分開發都是知道的,不清楚的百度學習一波。

簡單的說,Ribbon是Netflix發佈的開源項目,主要功能是提供客戶端的軟體負載均衡演算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡演算法。

Ribbon的配置在上一篇中已經給出,實現也是非常的簡單,主要看幾種負載均衡演算法:

策略名策略描述實現說明
BestAvailableRule 選擇一個最小的併發請求的server 逐個考察Server,如果Server被tripped了,則忽略,在選擇其中ActiveRequestsCount最小的server
AvailabilityFilteringRule 過濾掉那些因為一直連接失敗的被標記為circuit tripped的後端server,並過濾掉那些高併發的的後端server(active connections 超過配置的閾值) 使用一個AvailabilityPredicate來包含過濾server的邏輯,其實就就是檢查status里記錄的各個server的運行狀態
WeightedResponseTimeRule 根據相應時間分配一個weight,相應時間越長,weight越小,被選中的可能性越低。 一個後臺線程定期的從status裡面讀取評價響應時間,為每個server計算一個weight。Weight的計算也比較簡單responsetime 減去每個server自己平均的responsetime是server的權重。當剛開始運行,沒有形成statas時,使用roubine策略選擇server。
RetryRule 對選定的負載均衡策略機上重試機制。 在一個配置時間段內當選擇server不成功,則一直嘗試使用subRule的方式選擇一個可用的server
RoundRobinRule roundRobin方式輪詢選擇server 輪詢index,選擇index對應位置的server
RandomRule 隨機選擇一個server 在index上隨機,選擇index對應位置的server
ZoneAvoidanceRule 複合判斷server所在區域的性能和server的可用性選擇server 使用ZoneAvoidancePredicate和AvailabilityPredicate來判斷是否選擇某個server,前一個判斷判定一個zone的運行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用於過濾掉連接數過多的Server。

當想要修改負載均衡的策略時,直接返回IRule實現即可,例:

@Bean
public IRule myRule(){
    return new RandomRule();
}

四、Feign

Feign 是一個聲明式的偽 Http 客戶端,它使得寫 Http 客戶端變得更簡單。使用 Feign,只需要創建一個介面並註解。它具有可插拔的註解特性,可使用 Feign 註解和 JAX-RS 註解。Feign 支持可插拔的編碼器和解碼器。Feign 預設集成了 Ribbon,並和 Eureka 結合,預設實現了負載均衡的效果。

Feign旨在使編寫Java Http客戶端變得更容易。前面在使用Ribbon+RestTemplate時,利用RestTemplate對http請求的封裝處理,形成了一套模版化的調用方法。但是在實際開發中,由於對服務依賴的調用可能不止一處,往往一個介面會被多處調用,所以通常都會針對每個微服務自行封裝一些客戶端類來包裝這些依賴服務的調用。所以,Feign在此基礎上做了進一步封裝,由他來幫助我們定義和實現依賴服務介面的定義。在Feign的實現下,我們只需創建一個介面並使用註解的方式來配置它(以前是Dao介面上面標註Mapper註解,現在是一個微服務介面上面標註一個Feign註解即可),即可完成對服務提供方的介面綁定,簡化了使用Spring cloud Ribbon時,自動封裝服務調用客戶端的開發量。

來實踐感受下:跟之前一樣再創建一個項目spring-cloud-learn-consumer-dept-feign,並新建pom.xml文件,手動添加到maven中:

<?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>

    <parent>
        <groupId>com.yuanqinnan</groupId>
        <artifactId>spring-cloud-learn-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>spring-cloud-learn-consumer-dept-feign</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Spring Boot Begin -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot End -->

        <!-- Spring Cloud Begin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- Spring Cloud End -->
    </dependencies>

</project>

創建啟動類:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerDeptFeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerDeptFeignApplication.class, args);
    }
}

application.yml:

spring:
  application:
    name: spring-cloud-learn-consumer-dept-feign

server:
  port: 8765

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

現在就是調用服務的操作,添加一個部門服務:

@FeignClient(value = "spring-cloud-learn-provider-dept")
public interface DeptService {

    @RequestMapping(value = "hi", method = RequestMethod.GET)
    String sayHi(@RequestParam(value = "message") String message);
}

這裡添加的@FeignClient的註解裡面的value屬性就是調用服務的名稱,而裡面的方法的寫法與WebMVC的寫法很類似,相當於調用服務的方法

然後再創建一個controller,,然後創建方法調用

@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    @RequestMapping(value = "hi", method = RequestMethod.GET)
    public String sayHi(@RequestParam String message) {
        return deptService.sayHi(message);
    }
}

這個時候編碼已經完成,我們只需要啟動之前項目中的spring-cloud-learn-eureka與spring-cloud-learn-provider-dept,然後再啟動本項目,訪問地址:http://localhost:8765/hi?message=HelloFeign,這個時候服務就已經生效了:

這樣就已經完成Feign組件的使用,非常的簡單, Feign通過介面的方法調用Rest服務(之前是Ribbon+RestTemplate),該請求發送給Eureka伺服器,通過Feign直接找到服務介面,由於在進行服務調用的時候融合了Ribbon技術,所以也支持負載均衡作用。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一:MonoDB的簡單介紹 MongoDB是一個介於關係型資料庫與非關係型資料庫中間的資料庫,是使用C++進行編寫的,他的優點是在支持的查詢格式特別的強大,可以進行存儲比較複雜的數據類型,支持建立索引 二:下載 官方地址:https://www.mongodb.com/ 本教程下載 3.4版本:ht ...
  • Python分散式爬蟲必學框架Scrapy打造搜索引擎 部分課程截圖: 點擊鏈接或搜索QQ號直接加群獲取其它資料: 鏈接:https://pan.baidu.com/s/1-wHr4dTAxfd51Mj9DxiJ4Q 提取碼:ik1n 免費分享,如若鏈接失效請加群 其它資源在群里,私聊管理員即可免費 ...
  • 又是兩個月的時間過去了,上一次寫博客是7月14號,時間還是過的很快的,那麼問題來了,為什麼這麼長時間都沒有寫東西了呢?難道是在打醬油? 哈哈,說起來很慚愧,剛剛開始工作,碰到各種的問題要去學習要去解決,然後業餘的時間又去學了一些奇奇怪怪的東西,導致博客一直都落下了,歸根到底,還是自己懶惰了,因為心中 ...
  • 面試題 dubbo 的 spi 思想是什麼? 面試官心理分析 繼續深入問唄,前面一些基礎性的東西問完了,確定你應該都 ok,瞭解 dubbo 的一些基本東西,那麼問個稍微難一點點的問題,就是 spi,先問問你 spi 是啥?然後問問你 dubbo 的 spi 是怎麼實現的? 其實就是看看你對 dub ...
  • AOP簡介 今天來介紹一下AOP。AOP,中文常被翻譯為“面向切麵編程”,其作為OOP的擴展,其思想除了在Spring中得到了應用,也是不錯的設計方法。通常情況下,一個軟體系統,除了正常的業務邏輯代碼,往往還有一些功能性的代碼,比如:記錄日誌、數據校驗等等。最原始的辦法就是直接在你的業務邏輯代碼中編 ...
  • 前言 介面是Spring中一個非常重要的介面,它的介面定義如下 當你實現了這個介面的時候,Spring會保證在每一個bean對象初始化方法調用之前調用 方法,在初始化方法調用之後調用 的註冊 看過我之前寫的IOC源碼分析系列文章的同學應該對這個都比較有印象 ) Spring在執行到這的時候會把所有實 ...
  • 使用資源綁定器綁定屬性配置 實際開發中不建議把連接資料庫的信息寫死到Java程式中 //使用資源綁定器綁定屬性配置 ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String driver = bundle.getString(" ...
  • 1.每個service的impl都可以指定名稱(使用@Service(“名稱”)) 2.Controller中註入service的時候使用名稱來指定註入哪一個。 (1). (2). 代碼如下: ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...