<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Cloud OpenFeign奪命連環(huán)9問,這誰受得了?

          共 10599字,需瀏覽 22分鐘

           ·

          2022-02-18 19:27

          1、前言

          前面介紹了Spring Cloud 中的靈魂擺渡者Nacos,和它的前輩們相比不僅僅功能強大,而且部署非常簡單。

          今天介紹一款服務調(diào)用的組件:OpenFeign,同樣是一款超越先輩(Ribbon、Feign)的狠角色。

          文章目錄如下:

          2、Feign是什么?

          Feign也是一個狠角色,F(xiàn)eign旨在使得Java Http客戶端變得更容易。

          Feign集成了Ribbon、RestTemplate實現(xiàn)了負載均衡的執(zhí)行Http調(diào)用,只不過對原有的方式(Ribbon+RestTemplate)進行了封裝,開發(fā)者不必手動使用RestTemplate調(diào)服務,而是定義一個接口,在這個接口中標注一個注解即可完成服務調(diào)用,這樣更加符合面向接口編程的宗旨,簡化了開發(fā)。

          但遺憾的是Feign現(xiàn)在停止迭代了,當然現(xiàn)在也是有不少企業(yè)在用。

          有想要學習Feign的讀者可以上spring Cloud官網(wǎng)學習,陳某這里也不再詳細介紹了,不是今天的重點。

          3、openFeign是什么?

          前面介紹過停止迭代的Feign,簡單點來說:OpenFeign是springcloud在Feign的基礎上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通過動態(tài)代理的方式產(chǎn)生實現(xiàn)類,實現(xiàn)類中做負載均衡并調(diào)用其他服務。

          官網(wǎng)地址:https://docs.spring.io/spring-cloud-openfeign/docs/2.2.10.BUILD-SNAPSHOT/reference/html

          4、Feign和openFeign有什么區(qū)別?

          FeignopenFiegn
          Feign是SpringCloud組件中一個輕量級RESTful的HTTP服務客戶端,F(xiàn)eign內(nèi)置了Ribbon,用來做客戶端負載均衡,去調(diào)用服務注冊中心的服務。Feign的使用方式是:使用Feign的注解定義接口,調(diào)用這個接口,就可以調(diào)用服務注冊中心的服務OpenFeign 是SpringCloud在Feign的基礎上支持了SpringMVC的注解,如@RequestMapping等。OpenFeign 的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通過動態(tài)代理的方式產(chǎn)生實現(xiàn)類,實現(xiàn)類中做負載均衡并調(diào)用其他服務。

          5、環(huán)境準備

          本篇文章Spring Cloud版本、JDK環(huán)境、項目環(huán)境均和上一篇Nacos的環(huán)境相同:五十五張圖告訴你微服務的靈魂擺渡者Nacos究竟有多強?

          注冊中心就不再使用Eureka了,直接使用Nacos作為注冊和配置中心,有不會的可以查看Nacos文章。

          本篇文章搭建的項目結構如下圖:

          注冊中心使用Nacos,創(chuàng)建個微服務,分別為服務提供者Produce,服務消費者Consumer

          6、創(chuàng)建服務提供者

          既然是微服務之間的相互調(diào)用,那么一定會有服務提供者了,創(chuàng)建openFeign-provider9005,注冊進入Nacos中,配置如下:

          server:
          ??port:?9005
          spring:
          ??application:
          ????##?指定服務名稱,在nacos中的名字
          ????name:?openFeign-provider
          ??cloud:
          ????nacos:
          ??????discovery:
          ????????#?nacos的服務地址,nacos-server中IP地址:端口號
          ????????server-addr:?127.0.0.1:8848
          management:
          ??endpoints:
          ????web:
          ??????exposure:
          ????????##?yml文件中存在特殊字符,必須用單引號包含,否則啟動報錯
          ????????include:?'*'

          注意:此處的spring.application.name指定的名稱將會在openFeign接口調(diào)用中使用。

          項目源碼都會上傳,關于如何注冊進入Nacos,添加什么依賴源碼都會有,結合陳某上篇Nacos文章,這都不是難事!

          7、創(chuàng)建服務消費者

          新建一個模塊openFeign-consumer9006作為消費者服務,步驟如下。

          1、添加依賴

          除了Nacos的注冊中心的依賴,還要添加openFeign的依賴,如下:

          <dependency>
          ??????<groupId>org.springframework.cloudgroupId>
          ??????<artifactId>spring-cloud-starter-openfeignartifactId>
          dependency>

          2、添加注解@EnableFeignClients開啟openFeign功能

          老套路了,在Spring boot 主啟動類上添加一個注解@EnableFeignClients,開啟openFeign功能,如下:

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

          3、新建openFeign接口

          新建一個openFeign接口,使用@FeignClient注解標注,如下:

          @FeignClient(value?=?"openFeign-provider")
          public?interface?OpenFeignService?{
          }

          注意:該注解@FeignClient中的value屬性指定了服務提供者在nacos注冊中心的服務名

          4、新建一個Controller調(diào)試

          新建一個controller用來調(diào)試接口,直接調(diào)用openFeign的接口,如下:

          @RestController
          @RequestMapping("/openfeign")
          public?class?OpenFeignController?{
          ????
          }

          好了,至此一個openFeign的微服務就搭建好了,并未實現(xiàn)具體的功能,下面一點點實現(xiàn)。

          8、openFeign如何傳參?

          開發(fā)中接口傳參的方式有很多,但是在openFeign中的傳參是有一定規(guī)則的,下面詳細介紹。

          1、傳遞JSON數(shù)據(jù)

          這個也是接口開發(fā)中常用的傳參規(guī)則,在Spring Boot 中通過@RequestBody標識入?yún)ⅰ?/p>

          provider接口中JSON傳參方法如下:

          @RestController
          @RequestMapping("/openfeign/provider")
          public?class?OpenFeignProviderController?{
          ????@PostMapping("/order2")
          ????public?Order?createOrder2(@RequestBody?Order?order){
          ????????return?order;
          ????}
          }

          consumer中openFeign接口中傳參代碼如下:

          @FeignClient(value?=?"openFeign-provider")
          public?interface?OpenFeignService?{
          ????/**
          ?????*?參數(shù)默認是@RequestBody標注的,這里的@RequestBody可以不填
          ?????*?方法名稱任意
          ?????*/

          ????@PostMapping("/openfeign/provider/order2")
          ????Order?createOrder2(@RequestBody?Order?order);
          }

          注意:openFeign默認的傳參方式就是JSON傳參(@RequestBody),因此定義接口的時候可以不用@RequestBody注解標注,不過為了規(guī)范,一般都填上。

          2、POJO表單傳參

          這種傳參方式也是比較常用,參數(shù)使用POJO對象接收。

          provider服務提供者代碼如下:

          @RestController
          @RequestMapping("/openfeign/provider")
          public?class?OpenFeignProviderController?{
          ????@PostMapping("/order1")
          ????public?Order?createOrder1(Order?order){
          ????????return?order;
          ????}
          }

          consumer消費者openFeign代碼如下:

          @FeignClient(value?=?"openFeign-provider")
          public?interface?OpenFeignService?{
          ????/**
          ?????*?參數(shù)默認是@RequestBody標注的,如果通過POJO表單傳參的,使用@SpringQueryMap標注
          ?????*/

          ????@PostMapping("/openfeign/provider/order1")
          ????Order?createOrder1(@SpringQueryMap?Order?order);
          }

          網(wǎng)上很多人疑惑POJO表單方式如何傳參,官方文檔明確給出了解決方案,如下:

          openFeign提供了一個注解@SpringQueryMap完美解決POJO表單傳參。

          3、URL中攜帶參數(shù)

          此種方式針對restful方式中的GET請求,也是比較常用請求方式。

          provider服務提供者代碼如下:

          @RestController
          @RequestMapping("/openfeign/provider")
          public?class?OpenFeignProviderController?{

          ????@GetMapping("/test/{id}")
          ????public?String?test(@PathVariable("id")Integer?id){
          ????????return?"accept?one?msg?id="+id;
          }

          consumer消費者openFeign接口如下:

          @FeignClient(value?=?"openFeign-provider")
          public?interface?OpenFeignService?{

          ????@GetMapping("/openfeign/provider/test/{id}")
          ????String?get(@PathVariable("id")Integer?id);
          }

          使用注解@PathVariable接收url中的占位符,這種方式很好理解。

          4、普通表單參數(shù)

          此種方式傳參不建議使用,但是也有很多開發(fā)在用。

          provider服務提供者代碼如下:

          @RestController
          @RequestMapping("/openfeign/provider")
          public?class?OpenFeignProviderController?{
          ????@PostMapping("/test2")
          ????public?String?test2(String?id,String?name){
          ????????return?MessageFormat.format("accept?on?msg?id={0},name={1}",id,name);
          ????}
          }

          consumer消費者openFeign接口傳參如下:

          @FeignClient(value?=?"openFeign-provider")
          public?interface?OpenFeignService?{
          ????/**
          ?????*?必須要@RequestParam注解標注,且value屬性必須填上參數(shù)名
          ?????*?方法參數(shù)名可以任意,但是@RequestParam注解中的value屬性必須和provider中的參數(shù)名相同
          ?????*/

          ????@PostMapping("/openfeign/provider/test2")
          ????String?test(@RequestParam("id")?String?arg1,@RequestParam("name")?String?arg2);
          }

          5、總結

          傳參的方式有很多,比如文件傳參.....陳某這里只是列舉了四種常見得傳參方式。

          9、超時如何處理?

          想要理解超時處理,先看一個例子:我將provider服務接口睡眠3秒鐘,接口如下:

          @PostMapping("/test2")
          public?String?test2(String?id,String?name)?throws?InterruptedException?{
          ????????Thread.sleep(3000);
          ????????return?MessageFormat.format("accept?on?msg?id={0},name={1}",id,name);
          }

          此時,我們調(diào)用consumer的openFeign接口返回結果如下圖:

          很明顯的看出程序異常了,返回了接口調(diào)用超時。what?why?...........

          openFeign其實是有默認的超時時間的,默認分別是連接超時時間10秒、讀超時時間60秒,源碼在feign.Request.Options#Options()這個方法中,如下圖:

          那么問題來了:為什么我只設置了睡眠3秒就報超時呢?

          其實openFeign集成了Ribbon,Ribbon的默認超時連接時間、讀超時時間都是是1秒,源碼在org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute()方法中,如下圖:

          源碼大致意思:如果openFeign沒有設置對應得超時時間,那么將會采用Ribbon的默認超時時間。

          理解了超時設置的原理,由之產(chǎn)生兩種方案也是很明了了,如下:

          • 設置openFeign的超時時間
          • 設置Ribbon的超時時間

          1、設置Ribbon的超時時間(不推薦)

          設置很簡單,在配置文件中添加如下設置:

          ribbon:
          ??#?值的是建立鏈接所用的時間,適用于網(wǎng)絡狀況正常的情況下,?兩端鏈接所用的時間
          ??ReadTimeout:?5000
          ??#?指的是建立鏈接后從服務器讀取可用資源所用的時間
          ??ConectTimeout:?5000

          2、設置openFeign的超時時間(推薦)

          openFeign設置超時時間非常簡單,只需要在配置文件中配置,如下:

          feign:
          ??client:
          ????config:
          ??????##?default?設置的全局超時時間,指定服務名稱可以設置單個服務的超時時間
          ??????default:
          ????????connectTimeout:?5000
          ????????readTimeout:?5000

          default設置的是全局超時時間,對所有的openFeign接口服務都生效

          但是正常的業(yè)務邏輯中可能涉及到多個openFeign接口的調(diào)用,如下圖:

          上圖中的偽代碼如下:

          public?T?invoke(){
          ????//1.?調(diào)用serviceA
          ????serviceA();
          ????
          ????//2.?調(diào)用serviceA
          ????serviceB();
          ????
          ????//3.?調(diào)用serviceA
          ????serviceC();
          }

          那么上面配置的全局超時時間能不能通過呢?很顯然是serviceA、serviceB能夠成功調(diào)用,但是serviceC并不能成功執(zhí)行,肯定報超時。

          此時我們可以給serviceC這個服務單獨配置一個超時時間,配置如下:

          feign:
          ??client:
          ????config:
          ??????##?default?設置的全局超時時間,指定服務名稱可以設置單個服務的超時時間
          ??????default:
          ????????connectTimeout:?5000
          ????????readTimeout:?5000
          ??????##?為serviceC這個服務單獨配置超時時間
          ??????serviceC:
          ????????connectTimeout:?30000
          ????????readTimeout:?30000

          注意:單個配置的超時時間將會覆蓋全局配置。

          10、如何開啟日志增強?

          openFeign雖然提供了日志增強功能,但是默認是不顯示任何日志的,不過開發(fā)者在調(diào)試階段可以自己配置日志的級別。

          openFeign的日志級別如下:

          • NONE:默認的,不顯示任何日志;
          • BASIC:僅記錄請求方法、URL、響應狀態(tài)碼及執(zhí)行時間;
          • HEADERS:除了BASIC中定義的信息之外,還有請求和響應的頭信息;
          • FULL:除了HEADERS中定義的信息之外,還有請求和響應的正文及元數(shù)據(jù)。

          配置起來也很簡單,步驟如下:

          1、配置類中配置日志級別

          需要自定義一個配置類,在其中設置日志級別,如下:

          注意:這里的logger是feign包里的。

          2、yaml文件中設置接口日志級別

          只需要在配置文件中調(diào)整指定包或者openFeign的接口日志級別,如下:

          logging:
          ??level:
          ????cn.myjszl.service:?debug

          這里的cn.myjszl.service是openFeign接口所在的包名,當然你也可以配置一個特定的openFeign接口。

          3、演示效果

          上述步驟將日志設置成了FULL,此時發(fā)出請求,日志效果如下圖:

          日志中詳細的打印出了請求頭、請求體的內(nèi)容。

          11、如何替換默認的httpclient?

          Feign在默認情況下使用的是JDK原生的URLConnection發(fā)送HTTP請求,沒有連接池,但是對每個地址會保持一個長連接,即利用HTTP的persistence connection。

          在生產(chǎn)環(huán)境中,通常不使用默認的http client,通常有如下兩種選擇:

          • 使用ApacheHttpClient
          • 使用OkHttp

          至于哪個更好,其實各有千秋,我比較傾向于ApacheHttpClient,畢竟老牌子了,穩(wěn)定性不在話下。

          那么如何替換掉呢?其實很簡單,下面演示使用ApacheHttpClient替換。

          1、添加ApacheHttpClient依賴

          在openFeign接口服務的pom文件添加如下依賴:


          ????<dependency>
          ??????<groupId>org.apache.httpcomponentsgroupId>
          ??????<artifactId>httpclientartifactId>
          ????dependency>
          ????
          ????<dependency>
          ??????<groupId>io.github.openfeigngroupId>
          ??????<artifactId>feign-httpclientartifactId>
          ????dependency>

          為什么要添加上面的依賴呢?從源碼中不難看出,請看org.springframework.cloud.openfeign.FeignAutoConfiguration.HttpClientFeignConfiguration這個類,代碼如下:

          上述紅色框中的生成條件,其中的@ConditionalOnClass(ApacheHttpClient.class),必須要有ApacheHttpClient這個類才會生效,并且feign.httpclient.enabled這個配置要設置為true

          2、配置文件中開啟

          在配置文件中要配置開啟,代碼如下:

          feign:
          ??client:
          ????httpclient:
          ??????#?開啟?Http?Client
          ??????enabled:?true

          3、如何驗證已經(jīng)替換成功?

          其實很簡單,在feign.SynchronousMethodHandler#executeAndDecode()這個方法中可以清楚的看出調(diào)用哪個client,如下圖:

          上圖中可以看到最終調(diào)用的是ApacheHttpClient。

          4、總結

          上述步驟僅僅演示一種替換方案,剩下的一種不再演示了,原理相同。

          12、如何通訊優(yōu)化?

          在講如何優(yōu)化之前先來看一下GZIP 壓縮算法,概念如下:

          gzip是一種數(shù)據(jù)格式,采用用deflate算法壓縮數(shù)據(jù);gzip是一種流行的數(shù)據(jù)壓縮算法,應用十分廣泛,尤其是在Linux平臺。

          當GZIP壓縮到一個純文本數(shù)據(jù)時,效果是非常明顯的,大約可以減少70%以上的數(shù)據(jù)大小。

          網(wǎng)絡數(shù)據(jù)經(jīng)過壓縮后實際上降低了網(wǎng)絡傳輸?shù)淖止?jié)數(shù),最明顯的好處就是可以加快網(wǎng)頁加載的速度。網(wǎng)頁加載速度加快的好處不言而喻,除了節(jié)省流量,改善用戶的瀏覽體驗外,另一個潛在的好處是GZIP與搜索引擎的抓取工具有著更好的關系。例如 Google就可以通過直接讀取GZIP文件來比普通手工抓取更快地檢索網(wǎng)頁。

          GZIP壓縮傳輸?shù)脑砣缦聢D:

          按照上圖拆解出的步驟如下:

          • 客戶端向服務器請求頭中帶有:Accept-Encoding:gzip,deflate 字段,向服務器表示,客戶端支持的壓縮格式(gzip或者deflate),如果不發(fā)送該消息頭,服務器是不會壓縮的。
          • 服務端在收到請求之后,如果發(fā)現(xiàn)請求頭中含有Accept-Encoding字段,并且支持該類型的壓縮,就對響應報文壓縮之后返回給客戶端,并且攜帶Content-Encoding:gzip消息頭,表示響應報文是根據(jù)該格式壓縮過的。
          • 客戶端接收到響應之后,先判斷是否有Content-Encoding消息頭,如果有,按該格式解壓報文。否則按正常報文處理。

          openFeign支持請求/響應開啟GZIP壓縮,整體的流程如下圖:

          上圖中涉及到GZIP傳輸?shù)闹挥袃蓧K,分別是Application client -> Application Service、 Application Service->Application client

          注意:openFeign支持的GZIP僅僅是在openFeign接口的請求和響應,即是openFeign消費者調(diào)用服務提供者的接口。

          openFeign開啟GZIP步驟也是很簡單,只需要在配置文件中開啟如下配置:

          feign:
          ??##?開啟壓縮
          ??compression:
          ????request:
          ??????enabled:?true
          ??????##?開啟壓縮的閾值,單位字節(jié),默認2048,即是2k,這里為了演示效果設置成10字節(jié)
          ??????min-request-size:?10
          ??????mime-types:?text/xml,application/xml,application/json
          ????response:
          ??????enabled:?true

          上述配置完成之后,發(fā)出請求,可以清楚看到請求頭中已經(jīng)攜帶了GZIP壓縮,如下圖:

          13、如何熔斷降級?

          常見的熔斷降級框架有HystrixSentinel,openFeign默認支持的就是Hystrix,這個在官方文檔上就有體現(xiàn),畢竟是一奶同胞嘛,哈哈...........

          但是阿里的Sentinel無論是功能特性、簡單易上手等各方面都完全秒殺Hystrix,因此此章節(jié)就使用openFeign+Sentinel進行整合實現(xiàn)服務降級。

          說明:此處并不著重介紹Sentinel,陳某打算放在下一篇文章詳細介紹Sentinel的強大之處。

          1、添加Sentinel依賴

          openFeign-consumer9006消費者的pom文件添加sentinel依賴(由于使用了聚合模塊,不指定版本號),如下:

          <dependency>
          ??????<groupId>com.alibaba.cloudgroupId>
          ??????<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
          dependency>

          2、配置文件中開啟sentinel熔斷降級

          要想openFeign使用sentinel的降級功能,還需要在配置文件中開啟,添加如下配置:

          feign:
          ??sentinel:
          ????enabled:?true

          3、添加降級回調(diào)類

          這個類一定要和openFeign接口實現(xiàn)同一個類,如下圖:

          OpenFeignFallbackService這個是降級回調(diào)的類,一旦OpenFeignService中對應得接口出現(xiàn)了異常則會調(diào)用這個類中對應得方法進行降級處理。

          4、添加fallback屬性

          @FeignClient中添加fallback屬性,屬性值是降級回調(diào)的類,如下:

          @FeignClient(value?=?"openFeign-provider",fallback?=?OpenFeignFallbackService.class)
          public?interface?OpenFeignService?
          {}

          5、演示

          經(jīng)過如上4個步驟,openFeign的熔斷降級已經(jīng)設置完成了,此時演示下效果。

          通過postman調(diào)用http://localhost:9006/openfeign/order3這個接口,正常邏輯返回如下圖:

          現(xiàn)在手動造個異常,在服務提供的接口中拋出異常,如下圖:

          此時重新調(diào)用http://localhost:9006/openfeign/order3,返回如下圖:

          哦豁,可以很清楚的看到服務已經(jīng)成功降級調(diào)用,哦了,功能完成。

          注意:實際開發(fā)中返回結果應該根據(jù)架構統(tǒng)一定制,陳某這里只是為了演示方便,不要借鑒,哈哈。。。

          14、總結

          本篇文章主要面對初學者,深入的源碼以及熔斷降級放在后面詳細介紹,文中若有表述不清,錯誤的地方歡迎指正!


          往期推薦

          芭比扣了!Nacos中服務刪除不了,腫么辦?

          2022-02-17

          Spring Cloud Alibaba Nacos 服務注冊與發(fā)現(xiàn)功能實現(xiàn)!

          2022-02-07

          Spring Cloud Alibaba Nacos路由策略之保護閾值!

          2022-02-14


          求點贊、在看、分享三連
          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日日擼夜夜擼 | 久操视频在线观看免费视频 | 精品三级久久久 | 亚洲最新视频在线免费播放不卡网站 | 久久国产热视频 |