主頁 > 後端開發 > 服務消費者(RestTemplate+Ribbon+feign)

服務消費者(RestTemplate+Ribbon+feign)

2020-09-24 01:34:02 後端開發

負載均衡

? spring cloud 體系中,我們知道服務之間的呼叫是通過http協議進行呼叫的,注冊中心就是維護這些呼叫的服務的各個服務串列,在Spring中提供了RestTemplate,用于訪問Rest服務的客戶端,Spring Cloud體系中也是使用RestTemplate進行服務之間的呼叫,

? 負載均衡(Load Balance),通常指將請求分攤到各個操作單元上進行處理,精髓在于均衡,也就是平均,負載均衡在我們日常開發中也是經常聽到的,下面介紹幾種常見的負載均衡技術實作,

  1. Nginx

    ? Nginx是我們平時使用的較多的負載均衡技術實作,性能也是很不錯,一個http請求請求至Nginx服務器,Nginx服務器根據請求和一定的演算法將請求轉發至真正的服務器,以達到負載均衡的目的,至于Nginx里面具體的負載均衡演算法,了解過Nginx的人應該會比較熟悉,沒了解過的請自己去看吧,

  2. DNS決議

    ? 在DNS服務器上配置多個域名對應ip的記錄,一個域名可以對應多組的ip地址,DNS服務器在決議域名時根據相應的演算法,把通過域名的請求分配到合適的真實服務器上去,這樣也可以達到一定的負載均衡,不至于一個服務器的壓力過大,

RestTemplate

我們先看下官方是怎么定義的:

Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others. The RestTemplate offers templates for common scenarios by HTTP method, in addition to the generalized exchange and execute methods that support of less frequent cases.

? 我們可以看到,RestTemplate采用的是同步方式執行Http請求的類,底層使用的是JDK的HttpURLConnection或者ApacheHttpComponents的類別庫,或者其他的類別庫,RestTemplate還提供了模版使得開發人員能夠更簡單發送Http請求,

? RestTemplate中定義了很多和Rest資源互動的API,下面就介紹2個我們平時常用的GET和POST請求,在RestTemplate中是怎么請求的,

GET請求:

RequestEntity requestEntity = RequestEntity.get(new URI(uri)).build();
ResponseEntity<User> responseEntity2 = this.restTemplate.exchange(requestEntity, User.class);

exchage是一個通用的請求方法,接受一個RequestEntity物件,可以設定路徑,請求頭,請求資訊等,最后回傳一個ResponeseEntity物體,

當然GET請求也可以是getForEntity()getForObject()2中型別:

//getForEntity()
ResponseEntity<User> responseEntity = this.restTemplate.getForEntity(uri, User.class);
User user = responseEntity.getBody();
//getForObject()
User user = this.restTemplate.getForObject(uri, User.class);

POST請求:

HttpEntity<MultiValueMap> request = new HttpEntity<>(map, header);    ResponseEntity<String> exchangeResult = restTemplate.exchange(url, HttpMethod.POST, request, String.class);

map中存放post的資料,header里存放請求頭相關的資訊,最后回傳一個ResponeseEntity物體類,

同樣的,POST請求也是有著對應的getForObject()getForEntity()型別:

//postForObject()
User user = this.restTemplate.postForObject(uri, user, User.class);
//postForEntity()
ResponseEntity<User> responseEntity = this.restTemplate.postForEntity(uri, user, User.class);

請他的請求,也是類似,這里就不在此列舉了,感興趣的可以查看RestTesmplate的api

Ribbon

Spring Cloud Ribbon是基于Netflix Ribbon實作的客戶端負載均衡組件,區別于Nginx的服務端負載均衡的實作,在結合Spring Cloud Eureka組件使用時,ribbonServerList會被重寫,改為通過Eureka的注冊中心來獲取服務串列,可以通過簡單的幾行配置來實作客戶端的負載均衡,

接下來我們來使用Spring Cloud Ribbon來實作客戶端的負載均衡

提前專案準備:

1.Eureka:首先我們先啟動一個Eureka 服務端作為注冊中心
2.然后開啟2個服務,注冊到Eureka服務中去,我這里開啟了2個oauth服務注冊到Eureka服務端中
3.開始構建具有負載均衡功能的服務消費方apiGateWay

? 這里使用apiGateWay作為服務消費方的原因是我們應用一般都是需要權限管理,驗證登錄用戶的,這里apiGateWay通過呼叫oauth應用服務來驗證用戶的合法性,

  1. 首先添加依賴

    <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>
    
  2. 啟用服務發現客戶端

    這里apiGateWay作為服務消費方,就相當于http請求程序充當了客戶端的角色,被呼叫的服務就是服務端,先在客戶端的啟動類中宣告要使用的RestTemplate

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableZuulProxy
    @Slf4j
    public class ApiGatewayApplication {
        public static void main(String[] args){
            SpringApplication.run(ApiGatewayApplication.class);
            log.info("ApiGatewayApplication啟動");
        }
    
        @Bean
        @LoadBalanced
        RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
  3. 撰寫測驗類

    @RestController
    @Slf4j
    public class RibbonClient {
        @Autowired
        RestTemplate restTemplate;
    
        @GetMapping("/demo")
        public String ribbonClientDemo(String paramName){
            log.info("請求引數paramName:{}",paramName);
            return restTemplate.getForObject("http://oauth/demo?paramName="+paramName,String.class);
        }
    }
    
  4. 啟動應用,訪問http://localhost:38763/demo?paramName=demo

    請求的埠是:38765
    或者是
    請求的埠是:38764
    

    然后我們多次發起請求,看被呼叫的服務的日志,可以發現請求確實被均勻的分配到開啟的2個服務的,

    oauth(埠:38764)

    請求的引數是:demo
    serverPort:38764
    

    oauth(埠:38765)

    請求的引數是:demo
    serverPort:38765
    

    可以看到,2個服務被輪詢呼叫,Ribbon默認的均衡策略是以輪詢的方式去選擇服務器,

    1.RandomRule:隨機選取負載均衡策略,隨機Random物件,在所有服務實體中隨機找一個服務的索引號,然后從上線的服務中獲取對應的服務,
    2.RoundRobinRule:線性輪詢負載均衡策略,
    3.WeightedResponseTimeRule:回應時間作為選取權重的負載均衡策略,根據平均回應時間計算所有服務的權重,回應時間越短的服務權重越大,被選中的概率越高,剛啟動時,如果統計資訊不足,則使用線性輪詢策略,等資訊足夠時,再切換到WeightedResponseTimeRule,
    4.RetryRule:使用線性輪詢策略獲取服務,如果獲取失敗則在指定時間內重試,重新獲取可用服務,
    5.ClientConfigEnabledRoundRobinRule:默認通過線性輪詢策略選取服務,通過繼承該類,并且對choose方法進行重寫,可以實作更多的策略,繼承后保底使用RoundRobinRule策略,
    6.BestAvailableRule:繼承自ClientConfigEnabledRoundRobinRule,從所有沒有斷開的服務中,選取到目前為止請求數量最小的服務,
    7.PredicateBasedRule:抽象類,提供一個choose方法的模板,通過呼叫AbstractServerPredicate實作類的過濾方法來過濾出目標的服務,再通過輪詢方法選出一個服務,
    8.AvailabilityFilteringRule:按可用性進行過濾服務的負載均衡策略,會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態的服務,還有并發的連接數超過閾值的服務,然后對剩余的服務串列進行線性輪詢,
    9.ZoneAvoidanceRule:本身沒有重寫choose方法,用的還是抽象父類PredicateBasedRule的choose,
    

    如果沒有符合我們業務需求的,那么我們也可以根據業務需求自己實作一個IRule,可以通過繼承AbstractLoadBalancerRule來實作我們自己的負載均衡演算法:

    @Slf4j
    public class MyRule extends AbstractLoadBalancerRule {
    
        @Override
        public void initWithNiwsConfig(IClientConfig iClientConfig){
    
        }
    
        /**
         * 自定義均衡策略
         * 這里簡單實作回傳串列的第一個服務
         */
        @Override
        public Server choose(Object o) {
            log.info("key:" + o);
          	//獲取服務串列
            List<Server> allServers = getLoadBalancer().getAllServers();
            log.info(allServers.toString());
            return allServers.get(0);
        }
    }
    

    我們通過實作choose方法來實作我們自己的負載均衡演算法,getLoadBalancer().getAllServers()可以獲取到所有的服務資訊,然后我們可以根據自己的策略來確定選擇哪一個服務器,

    自定義策略之后呢,我們需要在服務呼叫方添加一個注解配置@FeignClient+配置類來修改spring cloud的默認配置,

    首先啟動類上添加注解:

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableZuulProxy
    @Slf4j
    @FeignClient(value = "https://www.cnblogs.com/mzhblog/p/oauth",configuration = RuleConfig.class)
    public class ApiGatewayApplication {
        public static void main(String[] args){
            SpringApplication.run(ApiGatewayApplication.class);
            log.info("ApiGatewayApplication啟動");
        }
    
        @Bean
        @LoadBalanced
        RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    

    然后創建RuleConfig類來把策略修改為我們剛剛實作的策略:

    @Configuration
    public class RuleConfig {
        @Bean
        public IRule ribbonRule() {
            return new MyRule();
        }
    }
    

    重新啟動應用,然后我們在發起請求,就會發現這次請求就只會轉發給同一個服務,也就是server串列里的第一個服務,而不在是輪詢的進行請求,

    然后可以看到MyRule類列印的日志:

    請求引數paramName:demo
    key:default
    [192.168.3.2:38764, 192.168.3.2:38765]
    

    服務串列中依舊是2個服務在運行,但請求通過我們自定義的策略,永遠只會發送給第一個服務,而不會轉發給第二個服務,

    當然我們也可以通過組態檔配置,這就自己去研究吧,,,

feign

我們先來看下官網是怎么定義的:

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka to provide a load balanced http client when using Feign.

Feign是一個宣告示的Http客戶端,它是的寫Http客戶端更加簡單,它只需要通過注解相應的介面就可以實作,雖然通過上面的Ribbon里介紹的也可以創建Http客戶端發起請求,但不是那么的優雅,Feign是NetFlix開發的宣告式、模塊式的HTTP客戶端,可以幫助我們更好的、更快的開發呼叫HTTP API,

Feign應用

  1. 首先在工程中加入相應的依賴:

    <!-- feign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 然后添加組態檔:

    server.port=38763
    spring.application.name=api-gateway
    spring.cloud.config.discovery.enabled=true
    spring.cloud.config.discovery.service-id=config-server
    [email protected]@
    eureka.client.service-url.defaultZone=http://localhost:38761/eureka/
    
  3. 創建啟動類,添加注解@EnableFeignClients,開啟Feign支持:

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableZuulProxy
    @Slf4j
    @EnableFeignClients
    public class ApiGatewayApplication {
        public static void main(String[] args){
            SpringApplication.run(ApiGatewayApplication.class);
            log.info("ApiGatewayApplication啟動");
        }
    }
    
  4. 創建一個介面類IOauthClient,加入注解@FeignClient來指定這個介面要呼叫的服務的名稱

    //使用@FeignClient注解來指定要呼叫的服務名稱,即注冊到eureka里的服務名稱
    @FeignClient(name = "oauth")
    public interface IOauthClient {
    
        /**
         * value 指定要呼叫的介面
         * method 指定呼叫的方式是 GET、POST、PUT、DELETE等
         * @RequestParam 指定要傳入的引數
         */
        @RequestMapping(value = "https://www.cnblogs.com/demo",method = RequestMethod.GET)
        public String demo(@RequestParam("paramName") String paramName);
    }
    
  5. 然后在業務需要的地方呼叫該介面

    @RestController
    @Slf4j
    public class DemoController {
    
        @Autowired
        IOauthClient iOauthClient;
    
        @GetMapping("/demo")
        public String demo(String paramName){
            log.info("請求引數為:{}",paramName);
            return iOauthClient.demo(paramName);
        }
    }
    
  6. 啟動應用,然后發起請求

    然后我們就可以看到頁面上請求的回傳值:

    請求的埠是:38764
    

    是不是和前面講Ribbon一樣的,不過這種實作方式是不是和前面的方式更加優雅呢?尤其是在呼叫的介面變多之后,這種實作方式是不是更加簡單?

  7. 注意

    這里使用Feign的時候,需要注意的問題:

    1.GET請求多個引數的時候,需要使用@RequestParam

    2.POST請求使用@RequestBody注解引數

參考資料

  • https://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign
  • https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html

歡迎關注我的公眾號

本文由博客群發一文多發等運營工具平臺 OpenWrite 發布

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/115579.html

標籤:Java

上一篇:什么是DO,DTO,VO,POJO

下一篇:java動態代理——jvm指令集基本概念和方法位元組碼結構的進一步探究及proxy原始碼分析四

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more