文章目錄
Feign 簡介
Spring Cloud的Feign支持的一箇中心概念就是命名客戶端.Feign客戶端使用@FeignClient註冊組合成組件,按需調用遠程服務器.
Spring Cloud使用FeignClientsConfiguration創建一個新的集合作爲每個命名客戶端的ApplicationContext(應用上下文), 包含feign.Decoder,feign.Encoder和feign.Contract.
你可以使用 Jersey 和 CXF 這些來寫一個 Rest 或 SOAP 服務的java客服端。你也可以直接使用 Apache HttpClient 來實現。但是 Feign 的目的是儘量的減少資源和代碼來實現和 HTTP API 的連接。通過自定義的編碼解碼器以及錯誤處理,你可以編寫任何基於文本的 HTTP API。
Feign 通過註解注入一個模板化請求進行工作。只需在發送之前關閉它,參數就可以被直接的運用到模板中。然而這也限制了 Feign,只支持文本形式的API,它在響應請求等方面極大的簡化了系統。同時,它也是十分容易進行單元測試的。
Spring Cloud應用在啓動時,Feign會掃描標有@FeignClient註解的接口,生成代理,並註冊到Spring容器中。生成代理時Feign會爲每個接口方法創建一個RequetTemplate對象,該對象封裝了HTTP請求需要的全部信息,請求參數名、請求方法等信息都是在這個過程中確定的,Feign的模板化就體現在這裏。
maven依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
Feign客戶端接口(消費者)
@FeignClient(value = "user", url = "${addr.url}")
public interface UserClient {
//feign獨有的註解方式
@RequestLine("GET /user/index")
String index();
@RequestMapping(value = "/get0/{id}", method = RequestMethod.GET)
User findById(@PathVariable("id") Long id);
@RequestMapping(value = "/get1", method = RequestMethod.GET)
User get1(@RequestParam("id") Long id, @RequestParam("name") String name);
@RequestMapping(value = "/get2", method = RequestMethod.GET)
User get2(@RequestParam Map<String, Object> map);
@RequestMapping(value = "/hello2", method=RequestMethod.GET)
User hello2(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
@RequestMapping(value = "/hello3", method=RequestMethod.POST)
String hello3(@RequestBody User user);
}
當前工程中有和Feign Client中一樣的Endpoint時,Feign Client的類上不能用@RequestMapping註解否則,當前工程該endpoint http請求且使用accpet時會報404.但是,如果不包含Accept header時,請求是可以的
或者像下面不在Feign Client上使用@RequestMapping註解,請求也是可以的,無論是否包含Accept:
@FeignClient(name = "card", url = "http://localhost:7913",
fallback = CardFeignClientFallback.class,
configuration = FeignClientConfiguration.class)
public interface CardFeignClient {
@RequestMapping(value = "/v1/card/balance", method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
Info info();
}
Feign將方法簽名中方法參數對象序列化爲請求參數放到HTTP請求中的過程,是由編碼器(Encoder)完成的。同理,將HTTP響應數據反序列化爲java對象是由解碼器(Decoder)完成的。
默認情況下,Feign會將標有@RequestParam註解的參數轉換成字符串添加到URL中,將沒有註解的參數通過Jackson轉換成json放到請求體中。注意,如果在@RequetMapping中的method將請求方式指定爲POST,那麼所有未標註解的參數將會被忽略,例如:
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
void update(@PathVariable("groupId") Integer groupId,
@RequestParam("groupName") String groupName,
DataObject obj);
此時因爲聲明的是GET請求沒有請求體,所以obj參數就會被忽略。
在Spring Cloud環境下,Feign的Encoder只會用來編碼沒有添加註解的參數。如果你自定義了Encoder, 那麼只有在編碼obj參數時纔會調用你的Encoder。對於Decoder, 默認會委託給SpringMVC中的MappingJackson2HttpMessageConverter類進行解碼。只有當狀態碼不在200 ~ 300之間時ErrorDecoder纔會被調用。ErrorDecoder的作用是可以根據HTTP響應信息返回一個異常,該異常可以在調用Feign接口的地方被捕獲到。我們目前就通過ErrorDecoder來使Feign接口拋出業務異常以供調用者處理
Feign在默認情況下使用的是JDK原生的URLConnection發送HTTP請求,沒有連接池,但是對每個地址會保持一個長連接,即利用HTTP的persistence connection 。我們可以用Apache的HTTP Client替換Feign原始的http client, 從而獲取連接池、超時時間等與性能息息相關的控制能力。Spring Cloud從Brixtion.SR5版本開始支持這種替換,首先在項目中聲明Apache HTTP Client和feign-httpclient依賴:
<!-- 使用Apache HttpClient替換Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign-httpclient}</version>
</dependency>
然後在application.properties中添加
feign.httpclient.enabled=true
配置文件application.yml
#feign
feign:
hystrix:
enabled: true
httpclient:
enabled: true
addr:
url: http://10.164.13.166:8080/msg-center/v1/sms/send
配置類Configuration
使用了配置@Configuration參數,自己定義Configuration類來自定義FeignClientsConfiguration,並且Configuration類的類路徑不能在啓動類Application的掃描路徑下,否則會覆蓋該項目所有的Feign接口的默認配置
package com.spring.feigin.config;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EurekaConfiguration {
//配置只允許使用Feign自己的註解url方式:@RequestLine
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("zhihao.miao", "123456");
}
//配置eureka的登錄名和密碼
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("zhihao.miao", "123456");
}
}
定義主體啓動類
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
feign使用Hystrix
添加依賴
<!-- 整合hystrix,其實feign中自帶了hystrix,引入該依賴主要是爲了使用其中的hystrix-metrics-event-stream,用於dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
feign接口註解中,增加fallback指定類
@FeignClient (value = "${name}", url = "${addr.url}", fallback = UserFallBack.class)
指定類中處理熔斷的後續邏輯
@Slf4j
@Component
public class PosMemberClientFallBack implements PosMemberClient {
@Override
public String addMember(MemberDTO memberDTO) {
log.warn("調用會員服務失敗");
return ("調用服務失敗,熔斷”);
}
}
配置文件
#hystrix
hystrix:
command:
default:
execution:
isolation:
strategy: THREAD
thread:
timeoutInMilliseconds: 30000
threadpool:
default:
coreSize: 500 #缺省爲10
修改啓動類
在啓動類上添加@EnableHystrix 註解
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableHystrix
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}
配置的fallback class也必須在FeignClient Configuration中實例化,否則會報
java.lang.IllegalStateException: No fallback instance of type class異常。
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
//實例化fallback
@Bean
public HystrixClientFallback fb(){
return new HystrixClientFallback();
}
}
在Spring Cloud中,Feign和Ribbon在整合了Hystrix後,可能會出現首次調用失敗的問題,要如何解決該問題呢?
Hystrix默認的超時時間是1秒,如果超過這個時間尚未響應,將會進入fallback代碼。
而首次請求往往會比較慢(因爲Spring的懶加載機制,要實例化一些類),這個響應時間可能就大於1秒了。
解決方案 :
方法一
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
該配置是讓Hystrix的超時時間改爲5秒
方法二
hystrix.command.default.execution.timeout.enabled: false
該配置,用於禁用Hystrix的超時時間
方法三
feign.hystrix.enabled: fals
該配置,用於索性禁用feign的hystrix。該做法除非一些特殊場景,不推薦使用。
Feign的擴展配置
#Hystrix支持,如果爲true,hystrix庫必須在classpath中
feign.hystrix.enabled=false
#請求和響應GZIP壓縮支持
feign.compression.request.enabled=true
feign.compression.response.enabled=true
#支持壓縮的mime types
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
# 日誌支持
logging.level.project.user.UserClient: DEBUG