日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

05 | REST消息通信:如何使用 OpenFeign 简化服务间通信

發(fā)布時間:2024/10/6 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 05 | REST消息通信:如何使用 OpenFeign 简化服务间通信 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一講我們學(xué)習(xí)了 Ribbon 與 RestTemplate 兩個組件。Ribbon 提供了客戶端負(fù)載均衡,而 RestTemplate 則封裝了 HTTP 的通訊,簡化了發(fā)送請求的過程。兩者相輔相成構(gòu)建了服務(wù)間的高可用通信。

不過在使用后,你也應(yīng)該會發(fā)現(xiàn) RestTemplate,它只是對 HTTP 的簡單封裝,像 URL、請求參數(shù)、請求頭、請求體這些細(xì)節(jié)都需要我們自己處理,如此底層的操作都暴露出來這肯定不利于項(xiàng)目團(tuán)隊(duì)間協(xié)作,因此就需要一種封裝度更高、使用更簡單的技術(shù)屏蔽通信底層復(fù)雜度。好在 Spring Cloud 團(tuán)隊(duì)提供了 OpenFeign 技術(shù),大幅簡化了服務(wù)間高可用通信處理過程。本講將主要介紹三部分:

  • 介紹 Feign 與 OpenFeign;
  • 講解 OpenFeign 的使用辦法;
  • 講解生產(chǎn)環(huán)境 OpenFeign 的配置優(yōu)化。

Feign 與 OpenFeign

Spring Cloud OpenFeign 并不是獨(dú)立的技術(shù)。它底層基于 Netflix Feign,Netflix Feign 是 Netflix 設(shè)計(jì)的開源的聲明式 WebService 客戶端,用于簡化服務(wù)間通信。Netflix Feign 采用“接口+注解”的方式開發(fā),通過模仿 RPC 的客戶端與服務(wù)器模式(CS),采用接口方式開發(fā)來屏蔽網(wǎng)絡(luò)通信的細(xì)節(jié)。OpenFeign 則是在 Netflix Feign 的基礎(chǔ)上進(jìn)行封裝,結(jié)合原有 Spring MVC 的注解,對 Spring Cloud 微服務(wù)通信提供了良好的支持。使用 OpenFeign 開發(fā)的方式與開發(fā) Spring MVC Controller 頗為相似。下面我們通過代碼說明 OpenFeign 的各種開發(fā)技巧。

OpenFeign 的使用辦法

為了便于理解,我們模擬實(shí)際案例進(jìn)行說明。假設(shè)某電商平臺日常訂單業(yè)務(wù)中,為保證每一筆訂單不會超賣,在創(chuàng)建訂單前訂單服務(wù)(order-service)首先去倉儲服務(wù)(warehouse-service)檢查對應(yīng)商品 skuId(品類編號)的庫存數(shù)量是否足夠,庫存充足創(chuàng)建訂單,不存不足 App 前端提示“庫存不足”。

在這個業(yè)務(wù)中,訂單服務(wù)依賴于倉儲服務(wù),那倉儲服務(wù)就是服務(wù)提供者,訂單服務(wù)是服務(wù)消費(fèi)者。下面我們通過代碼還原這個場景。

首先,先創(chuàng)建倉儲服務(wù)(warehouse-service),倉儲服務(wù)作為提供者就是標(biāo)準(zhǔn)微服務(wù),使用 Spring Boot 開發(fā)。

第一步,利用 Spring Initializr 向?qū)?chuàng)建 warehouse-service 工程。確保在 pom.xml 引入以下依賴。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

第二步,編輯 application.yml 新增 Nacos 通信配置。

spring:application:name: warehouse-service #應(yīng)用/微服務(wù)名字cloud:nacos:discovery:server-addr: 192.168.31.102:8848 #nacos服務(wù)器地址username: nacos #用戶名密碼password: nacosserver:port: 80

第三步,創(chuàng)建 Stock 庫存類,用于保存庫存數(shù)據(jù)。

package com.lagou.warehouseservice.dto;//庫存商品對象public class Stock {private Long skuId; //商品品類編號private String title; //商品與品類名稱private Integer quantity; //庫存數(shù)量private String unit; //單位private String description; //描述信息//帶參構(gòu)造函數(shù)public Stock(Long skuId, String title, Integer quantity, String unit) {this.skuId = skuId;this.title = title;this.quantity = quantity;this.unit = unit;}//getter and setter省略...}

第四步,創(chuàng)建倉儲服務(wù)控制器 WarehouseController,通過 getStock() 方法傳入 skuId 編號則返回具體商品庫存數(shù)量,代碼中模擬 skuId 編號為 1101 的“紫色 128G iPhone 11”庫存 32 臺,而編號 1102 的“白色 256G iPhone 11”已沒有庫存。

package com.lagou.warehouseservice.controller;//省略 import 部分//倉儲服務(wù)控制器@RestControllerpublic class WarehouseController {/*** 查詢對應(yīng) skuId 的庫存狀況* @param skuId skuId* @return Stock 庫存對象*/@GetMapping("/stock")public Stock getStock(Long skuId){Map result = new HashMap();Stock stock = null;if(skuId == 1101l){//模擬有庫存商品stock = new Stock(1101l, "Apple iPhone 11 128GB 紫色", 32, "臺");stock.setDescription("Apple 11 紫色版對應(yīng)商品描述");}else if(skuId == 1102l){//模擬無庫存商品stock = new Stock(1101l, "Apple iPhone 11 256GB 白色", 0, "臺");stock.setDescription("Apple 11 白色版對應(yīng)商品描述");}else{//演示案例,暫不考慮無對應(yīng) skuId 的情況}return stock;}}

可以看到 WarehouseController 就是普通的 Spring MVC 控制器,對外暴露了 stock 接口,當(dāng)應(yīng)用啟動后,查看 Nacos 服務(wù)列表,已出現(xiàn) warehouse-service 實(shí)例。

訪問下面的 URL 可看到 Stock 對象 JSON 序列化數(shù)據(jù)。

http://192.168.31.111/stock?skuId=1101{skuId: 1101,title: "Apple iPhone 11 128GB 紫色",quantity : 32,unit: "臺",description:"Apple 11 紫色版對應(yīng)商品描述"}

至此,服務(wù)提供者 warehouse-service 示例代碼已開發(fā)完畢。下面我們要開發(fā)服務(wù)消費(fèi)者 order-service。

第一步,確保利用 Spring Initializr 創(chuàng)建 order-service 工程,確保 pom.xml 引入以下 3 個依賴。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.5.RELEASE</version></dependency>

這里關(guān)鍵在于服務(wù)消費(fèi)者依賴了 spring-cloud-starter-openfeign,在 Spring Boot 工程會自動引入 Spring Cloud OpenFeign 與 Netflix Feign 的 Jar 包。這里有個重要細(xì)節(jié),當(dāng)我們引入 OpenFeign 的時候,在 Maven 依賴中會出現(xiàn) netflix-ribbon 負(fù)載均衡器的身影。

沒錯,OpenFeign 為了保證通信高可用,底層也是采用 Ribbon 實(shí)現(xiàn)負(fù)載均衡,其原理與 Ribbon+RestTemplate 完全相同,只不過相較 RestTemplate,OpenFeign 封裝度更高罷了。

第二步,啟用 OpenFeign 需要在應(yīng)用入口 OrderServiceApplication 增加 @EnableFeignClients 注解,其含義為通知 Spring 啟用 OpenFeign 聲明式通信。

package com.lagou.orderservice;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableFeignClients //啟用OpenFeignpublic class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}}

第三步,默認(rèn) OpenFeign 并不需要任何配置,在 application.yml 配置好 Nacos 通信即可。

spring:application:name: order-servicecloud:nacos:discovery:server-addr: 192.168.31.102:8848username: nacospassword: nacosserver:port: 80

第四步,最重要的地方來了,創(chuàng)建OpenFeign的通信接口與響應(yīng)對象,這里先給出完整代碼。

package com.lagou.orderservice.feignclient;import com.lagou.orderservice.dto.Stock;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;@FeignClient("warehouse-service")public interface WarehouseServiceFeignClient {@GetMapping("/stock")public Stock getStock(@RequestParam("skuId") Long skuId);}

在 order-service 工程下,創(chuàng)建一個 feignclient 包用于保存通信接口。OpenFeign 通過“接口+注解”形式描述數(shù)據(jù)傳輸邏輯,并不需要程序員編寫具體實(shí)現(xiàn)代碼便能實(shí)現(xiàn)服務(wù)間高可用通信,下面我們來學(xué)習(xí)這段代碼:

  • @FeignClient 注解說明當(dāng)前接口為 OpenFeign 通信客戶端,參數(shù)值 warehouse-service 為服務(wù)提供者 ID,這一項(xiàng)必須與 Nacos 注冊 ID 保持一致。在 OpenFeign 發(fā)送請求前會自動在 Nacos 查詢 warehouse-service 所有可用實(shí)例信息,再通過內(nèi)置的 Ribbon 負(fù)載均衡選擇一個實(shí)例發(fā)起 RESTful 請求,進(jìn)而保證通信高可用。
  • 聲明的方法結(jié)構(gòu),接口中定義的方法通常與服務(wù)提供者的方法定義保持一致。這里有個非常重要的細(xì)節(jié):用于接收數(shù)據(jù)的 Stock 對象并不強(qiáng)制要求與提供者端 Stock 對象完全相同,消費(fèi)者端的 Stock 類可以根據(jù)業(yè)務(wù)需要刪減屬性,但屬性必須要與提供者響應(yīng)的 JSON 屬性保持一致。距離說明,我們在代碼發(fā)現(xiàn)消費(fèi)者端 Stock 的包名與代碼與提供者都不盡相同,而且因?yàn)橄M(fèi)者不需要 description 屬性便將其刪除,其余屬性只要保證與服務(wù)提供者響應(yīng) JSON 保持一致,在 OpenFeign 獲取響應(yīng)后便根據(jù) JSON 屬性名自動反序列化到 Stock 對象中。
#服務(wù)提供者返回的響應(yīng){skuId: 1101,title: "Apple iPhone 11 128GB 紫色",quantity: 32,unit: "臺",description: "Apple 11 紫色版對應(yīng)商品描述"} package com.lagou.orderservice.dto;//消費(fèi)者端接收響應(yīng)Stock對象public class Stock {private Long skuId; //商品品類編號private String title; //商品與品類名稱private Integer quantity; //庫存數(shù)量private String unit; //單位@Overridepublic String toString() {return "Stock{" +"skuId=" + skuId +", title='" + title + '\'' +", quantity=" + quantity +", unit='" + unit + '\'' +'}';}//getter與setter省略}
  • @GetMapping/@PostMapping,以前我們在編寫 Spring MVC 控制器時經(jīng)常使用 @GetMapping 或者@ PostMapping 聲明映射方法的請求類型。雖然 OpenFeign 也使用了這些注解,但含義完全不同。在消費(fèi)者端這些注解的含義是:OpenFeign 向服務(wù)提供者 warehouse-service 的 stock 接口發(fā)起 Get 請求。簡單總結(jié)下,如果在服務(wù)提供者書寫 @GetMapping 是說明 Controller 接收數(shù)據(jù)的請求類型必須是 Get,而寫在消費(fèi)者端接口中則說明 OpenFeign 采用 Get 請求發(fā)送數(shù)據(jù),大多數(shù)情況下消費(fèi)者發(fā)送的請求類型、URI 與提供者定義要保持一致。
  • @RequestParam,該注解說明方法參數(shù)與請求參數(shù)之間的映射關(guān)系。舉例說明,當(dāng)調(diào)用接口的 getStock() 方法時 skuId 參數(shù)值為 1101,那實(shí)際通信時 OpenFeign 發(fā)送的 Get 請求格式就是:
http://warehouse-service可用實(shí)例 ip:端口/stock?skuId=1101

介紹每一個細(xì)節(jié)后,我用自然語言完整描述處理邏輯:

  • 在第一次訪問 WarehouseServiceFeignClient 接口時,Spring 自動生成接口的實(shí)現(xiàn)類并實(shí)例化對象。
  • 當(dāng)調(diào)用 getStock() 方法時,Ribbon 獲取 warehouse-service 可用實(shí)例信息,根據(jù)負(fù)載均衡策略選擇合適實(shí)例。
  • OpenFeign 根據(jù)方法上注解描述的映射關(guān)系生成完整的 URL 并發(fā)送 HTTP 請求,如果請求方法是 @PostMapping,則參數(shù)會附加在請求體中進(jìn)行發(fā)送。
  • http://warehouse-service 可用實(shí)例 ip:端口/stock?skuId=xxx

    4.warehouse-service 處理完畢返回 JSON 數(shù)據(jù),消費(fèi)者端 OpenFeign 接收 JSON 的同時反序列化到 Stock 對象,并將該對象返回。
    到這里我們花了較大的篇幅介紹 OpenFeign 的執(zhí)行過程,那該怎么使用呢?這就是第五步的事情了。

    第五步,在消費(fèi)者 Controller 中對 FeignClient 接口進(jìn)行注入,像調(diào)用本地方法一樣完成業(yè)務(wù)邏輯。

    package com.lagou.orderservice.controller;import com.lagou.orderservice.dto.Stock;import com.lagou.orderservice.feignclient.WarehouseServiceFeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import java.util.LinkedHashMap;import java.util.Map;@RestControllerpublic class OrderController {//利用@Resource將IOC容器中自動實(shí)例化的實(shí)現(xiàn)類對象進(jìn)行注入@Resourceprivate WarehouseServiceFeignClient warehouseServiceFeignClient;/*** 創(chuàng)建訂單業(yè)務(wù)邏輯* @param skuId 商品類別編號* @param salesQuantity 銷售數(shù)量* @return*/@GetMapping("/create_order")public Map createOrder(Long skuId , Long salesQuantity){Map result = new LinkedHashMap();//查詢商品庫存,像調(diào)用本地方法一樣完成業(yè)務(wù)邏輯。Stock stock = warehouseServiceFeignClient.getStock(skuId);System.out.println(stock);if(salesQuantity <= stock.getQuantity()){//創(chuàng)建訂單相關(guān)代碼,此處省略//CODE=SUCCESS代表訂單創(chuàng)建成功result.put("code" , "SUCCESS");result.put("skuId", skuId);result.put("message", "訂單創(chuàng)建成功");}else{//code=NOT_ENOUGN_STOCK代表庫存不足result.put("code", "NOT_ENOUGH_STOCK");result.put("skuId", skuId);result.put("message", "商品庫存數(shù)量不足");}return result;}}

    啟動后分別傳入不同 skuId 與銷售數(shù)量。可以看到 1101 商品庫存充足訂單創(chuàng)建成功,1102 商品因?yàn)闆]有庫存導(dǎo)致無法創(chuàng)建訂單。

    http://192.168.1.120/create_order?skuId=1101&salesQuantity=1{code: "SUCCESS", skuId: 1101, message: "訂單創(chuàng)建成功" }

    創(chuàng)建訂單成功消息

    http://192.168.1.120/create_order?skuId=1102&salesQuantity=1{code: "NOT_ENOUGH_STOCK", skuId: 1102, message: "商品庫存數(shù)量不足" }

    庫存數(shù)量不足錯誤提示

    到這里已經(jīng)基于 OpenFeign 實(shí)現(xiàn)了服務(wù)間通信。但事情還不算完,OpenFeign 默認(rèn)的配置并不能滿足生產(chǎn)環(huán)境的要求,下面咱們來講解在生產(chǎn)環(huán)境下 OpenFeign 還需要哪些必要的優(yōu)化配置。

    生產(chǎn)環(huán)境 OpenFeign 的配置事項(xiàng)

    如何更改 OpenFeign 默認(rèn)的負(fù)載均衡策略
    前面提到,在 OpenFeign 使用時默認(rèn)引用 Ribbon 實(shí)現(xiàn)客戶端負(fù)載均衡。那如何設(shè)置 Ribbon 默認(rèn)的負(fù)載均衡策略呢?在 OpenFeign 環(huán)境下,配置方式其實(shí)與之前 Ribbon+RestTemplate 方案完全相同,只需在 application.yml 中調(diào)整微服務(wù)通信時使用的負(fù)載均衡類即可。

    warehouse-service: #服務(wù)提供者的微服務(wù)IDribbon:#設(shè)置對應(yīng)的負(fù)載均衡類NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

    開啟默認(rèn)的 OpenFeign 數(shù)據(jù)壓縮功能
    在 OpenFeign 中,默認(rèn)并沒有開啟數(shù)據(jù)壓縮功能。但如果你在服務(wù)間單次傳遞數(shù)據(jù)超過 1K 字節(jié),強(qiáng)烈推薦開啟數(shù)據(jù)壓縮功能。默認(rèn) OpenFeign 使用 Gzip 方式壓縮數(shù)據(jù),對于大文本通常壓縮后尺寸只相當(dāng)于原始數(shù)據(jù)的 10%~30%,這會極大提高帶寬利用率。但有一種情況除外,如果應(yīng)用屬于計(jì)算密集型,CPU 負(fù)載長期超過 70%,因數(shù)據(jù)壓縮、解壓縮都需要 CPU 運(yùn)算,開啟數(shù)據(jù)壓縮功能反而會給 CPU 增加額外負(fù)擔(dān),導(dǎo)致系統(tǒng)性能降低,這是不可取的。

    feign:compression:request:# 開啟請求數(shù)據(jù)的壓縮功能enabled: true# 壓縮支持的MIME類型mime-types: text/xml,application/xml, application/json# 數(shù)據(jù)壓縮下限 1024表示傳輸數(shù)據(jù)大于1024 才會進(jìn)行數(shù)據(jù)壓縮(最小壓縮值標(biāo)準(zhǔn))min-request-size: 1024# 開啟響應(yīng)數(shù)據(jù)的壓縮功能response:enabled: true

    替換默認(rèn)通信組件

    OpenFeign 默認(rèn)使用 Java 自帶的 URLConnection 對象創(chuàng)建 HTTP 請求,但接入生產(chǎn)時,如果能將底層通信組件更換為 Apache HttpClient、OKHttp 這樣的專用通信組件,基于這些組件自帶的連接池,可以更好地對 HTTP 連接對象進(jìn)行重用與管理。作為 OpenFeign 目前默認(rèn)支持 Apache HttpClient 與 OKHttp 兩款產(chǎn)品。我以O(shè)KHttp配置方式為例,為你展現(xiàn)配置方法。

  • 引入 feign-okhttp 依賴包。
  • <dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId><version>11.0</version></dependency>
  • 在應(yīng)用入口,利用 Java Config 形式初始化 OkHttpClient 對象。
  • @SpringBootApplication@EnableFeignClientspublic class OrderServiceApplication {//Spring IOC容器初始化時構(gòu)建okHttpClient對象@Beanpublic okhttp3.OkHttpClient okHttpClient(){return new okhttp3.OkHttpClient.Builder()//讀取超時時間.readTimeout(10, TimeUnit.SECONDS)//連接超時時間.connectTimeout(10, TimeUnit.SECONDS)//寫超時時間.writeTimeout(10, TimeUnit.SECONDS)//設(shè)置連接池.connectionPool(new ConnectionPool()).build();}public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}}
  • 在 application.yml 中啟用 OkHttp。
  • feign:okhttp:enabled: true

    做到這里,我們已將OpenFeign的默認(rèn)通信對象從URLConnection調(diào)整為OKHttp,至于替換為HttpClient組件的配置思路是基本相同的。如果需要了解OpenFeign更詳細(xì)的配置選項(xiàng),可以訪問Spring Cloud OpenFeign的官方文檔進(jìn)行學(xué)習(xí)。
    https://docs.spring.io/spring-cloud-openfeign/docs/2.2.6.RELEASE/reference/html/

    總結(jié)

    以上是生活随笔為你收集整理的05 | REST消息通信:如何使用 OpenFeign 简化服务间通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。