目錄
一、SpringBoot快速整合Feign
1.添加Pom依賴(lài)
2.啟動(dòng)類(lèi)添加注解
3.引用Feign服務(wù)
二、為請(qǐng)求添加Header的3種方式
1.添加固定header
2.通過(guò)接口簽名添加header
3.動(dòng)態(tài)添加header
三、為請(qǐng)求添加超時(shí)配置
1.默認(rèn)超時(shí)時(shí)間
3.超時(shí)異常
4.全局超時(shí)配置
5.為單個(gè)服務(wù)設(shè)置超時(shí)配置
四、為請(qǐng)求配置客戶(hù)端負(fù)載均衡模式
五、Feign日志
1.日志級(jí)別
2.日志配置類(lèi)
3.配置文件
六、高階配置
七、Feign與Springboot版本
八、技術(shù)問(wèn)題
no suitable HttpMessageConverter found for response type
本章內(nèi)容講解重點(diǎn)目標(biāo)以及面向用戶(hù)
本章目標(biāo) | 了解feign的原理 掌握f(shuō)eign的使用 學(xué)會(huì)feign多種header的設(shè)置方式 學(xué)會(huì)feign日志控制 了解feign高階配置項(xiàng) 其他開(kāi)發(fā)注意事項(xiàng) |
面向用戶(hù) | 初、中、高階的研發(fā)同學(xué) |
很多時(shí)候,我們查看一些高階架構(gòu)同學(xué)在寫(xiě)Http服務(wù)調(diào)用時(shí),調(diào)試跟蹤代碼時(shí)發(fā)現(xiàn)就只有接口簽名,沒(méi)有想okhttp那樣寫(xiě)一些調(diào)用過(guò)程的代碼,代碼看起來(lái)風(fēng)格清爽、優(yōu)雅并不失邏輯嚴(yán)謹(jǐn),讀完該篇文章,您會(huì)掌握其實(shí)現(xiàn)方案,并提供demo協(xié)助您完成實(shí)戰(zhàn),有技術(shù)問(wèn)題可以私信請(qǐng)教;
一、SpringBoot快速整合Feign
1.添加Pom依賴(lài)
<properties>
<spring-cloud-feign.version>3.1.3</spring-cloud-feign.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring-cloud-feign.version}</version>
</dependency>
</dependencies>
2.啟動(dòng)類(lèi)添加注解
@EnableFeignClients
3.引用Feign服務(wù)
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
private LocalFeignService service;
@GetMapping("/get")
public String get(){
service.add("10");
return "Hello world";
}
}
二、為請(qǐng)求添加Header的3種方式
1.添加固定header
@RestController
public class HomeController {
@Resource
private FeignClientService feignClientService;
@RequestMapping(value = "/add")
public String add(@RequestBody FeignReq req) {
return "ok";
}
@RequestMapping("/test-add")
public Object testAdd() {
FeignReq req = new FeignReq();
req.setId(System.currentTimeMillis());
return feignClientService.add(req);
}
}
在@PostMapping注解的headers參數(shù)中添加固定的header值,也包括一些從配置文件讀取的配置項(xiàng)作為header值
@Component
@FeignClient(name = "feignClientService", url = "http://localhost:8081")
public interface FeignClientService {
@PostMapping(value = "/add", headers = {"Authorization=myFeignSignToken", "Content-Type=text/plain", "App=${my.name}"})
public String add(@RequestBody FeignReq req);
}
瀏覽器訪(fǎng)問(wèn):http://localhost:8081/test-add
2.通過(guò)接口簽名添加header
@RestController
public class HomeController {
@Resource
private FeignClientService feignClientService;
@PostMapping(value = "/query")
public String query(@RequestParam("queryName") String queryName) {
return queryName + "ok";
}
@RequestMapping("/test-query")
public Object testQuery() {
Map<String, String> map = new HashMap<>();
map.put("token", "ikong_token"+System.currentTimeMillis());
return feignClientService.query("ikong", map);
}
}
重點(diǎn)是在Map類(lèi)型的headers,這里通過(guò)接口簽名的@RequestHeader注解將Map對(duì)象轉(zhuǎn)化成請(qǐng)求header
@Component
@FeignClient(name = "feignClientService", url = "http://localhost:8081", configuration = TestRequestInterceptor.class)
public interface FeignClientService {
@PostMapping(value = "/query")
public String query(@RequestParam("queryName") String queryName, @RequestHeader Map<String, String> headers);
}
瀏覽器訪(fǎng)問(wèn):http://localhost:8081/test-query
3.動(dòng)態(tài)添加header
@RestController
public class HomeController {
@Resource
private FeignClientService feignClientService;
@RequestMapping("/search")
public Object search(@RequestBody FeignReq req) {
return "ok";
}
@RequestMapping("/test-search")
public Object testSearch() {
FeignReq req = new FeignReq();
req.setId(System.currentTimeMillis());
return feignClientService.search(req);
}
}
@Component
@FeignClient(name = "feignClientService", url = "http://localhost:8081", configuration = TestRequestInterceptor.class)
public interface FeignClientService {
@PostMapping("/search")
public String search(@RequestBody FeignReq req);
}
通過(guò)FeignClient注解注入configuration = TestRequestInterceptor.class,在TestRequestInterceptor實(shí)現(xiàn)header動(dòng)態(tài)添加能力
public class TestRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header(HttpHeaders.AUTHORIZATION, createApiSign());
}
/**
* 創(chuàng)建接口簽名
*
* @return
*/
private String createApiSign() {
return UUID.randomUUID().toString();
}
}
瀏覽器訪(fǎng)問(wèn):http://localhost:8081/test-search
三、為請(qǐng)求添加超時(shí)配置
1.默認(rèn)超時(shí)時(shí)間
connectiontimeout : 10s,readtimeout : 60s
public static class Options {
private final int connectTimeoutMillis;
private final int readTimeoutMillis;
public Options(int connectTimeoutMillis, int readTimeoutMillis) {
this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis;
}
public Options() {
this(10 * 1000, 60 * 1000);
}
/**
* Defaults to 10 seconds. {@code 0} implies no timeout.
*
* @see java.net.HttpURLConnection#getConnectTimeout()
*/
public int connectTimeoutMillis() {
return connectTimeoutMillis;
}
/**
* Defaults to 60 seconds. {@code 0} implies no timeout.
*
* @see java.net.HttpURLConnection#getReadTimeout()
*/
public int readTimeoutMillis() {
return readTimeoutMillis;
}
}
3.超時(shí)異常
exception:feign.RetryableException: connect timed out executing POST http://xxx.ik.com/your/api
4.全局超時(shí)配置
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
5.為單個(gè)服務(wù)設(shè)置超時(shí)配置
?要實(shí)現(xiàn)不同服務(wù)配備不同的超時(shí)時(shí)間,可按如下配置進(jìn)行
feign.client.config.yourService.connectTimeout=5
feign.client.config.yourService.readTimeout=5
四、為請(qǐng)求配置客戶(hù)端負(fù)載均衡模式
OpenFeign只是一種Rest客戶(hù)端,本身不具備任何的負(fù)載均衡操作。?OpenFeign底層調(diào)用的還是Netflix Ribbon負(fù)載均衡組件,那我們?cè)谑褂肙penFeign實(shí)現(xiàn)服務(wù)調(diào)用時(shí),如何修改負(fù)載均衡策略呢?
自定義Ribbon的負(fù)載均衡配置
@Configuration
// name為服務(wù)名
@RibbonClient(name = "my-provider", configuration = MyLoadBalanceConfiguration.class)
public class MyLoadBalanceConfiguration {
@Bean
public IRule ribbonRule() {
return new RandomRule(); // 采用隨機(jī)策略
}
}
我們這里使用的是隨機(jī)策略,默認(rèn)為輪詢(xún)策略。
Ribbon提供可選負(fù)載均衡分類(lèi)
- 隨機(jī)策略——RandomRule
- 輪詢(xún)策略——RoundRobinRule?注:Ribbon默認(rèn)策略
- 重試策略——RetryRule
- 最低并發(fā)策略——BestAvailableRule
- 可用過(guò)濾策略——AvailabilityFilteringRule、過(guò)濾掉那些因?yàn)橐恢边B接失敗的被標(biāo)記為circuit tripped的后端server,并過(guò)濾掉那些高并發(fā)的的后端server(active connections 超過(guò)配置的閾值),性能僅次于最低并發(fā)策略。
- 響應(yīng)時(shí)間加權(quán)策略——WeightedResponseTimeRule,每隔30秒計(jì)算一次服務(wù)器響應(yīng)時(shí)間,以響應(yīng)時(shí)間作為權(quán)重,響應(yīng)時(shí)間越短的服務(wù)器被選中的概率越大。
- 區(qū)域權(quán)衡策略——ZoneAvoidanceRule
Ribbon的負(fù)載均衡策略使用建議
一般情況下,推薦使用最低并發(fā)策略,這個(gè)性能比默認(rèn)的輪詢(xún)策略高很多。
五、Feign日志
feign的日志是建立在 debug日志模式的基礎(chǔ)上的,不管是全局配置還是局部配置,代碼配置還是屬性配置,都需要先把日志模式調(diào)成debug模式:(下面與調(diào)用FeignClientService接口的日志模式為例)
1.日志級(jí)別
OpenFeign的日志級(jí)別有
NONE: 默認(rèn)的,不顯示任何日志。
BASIC: 僅記錄請(qǐng)求方法、URL、響應(yīng)狀態(tài)碼以及執(zhí)行時(shí)間。
HEADERS:除了BASIC 中自定義的信息外,還有請(qǐng)求和響應(yīng)的信息頭。
FULL: 除了HEADERS中定義的信息外, 還有請(qǐng)求和響應(yīng)的正文以及元數(shù)據(jù)。
2.日志配置類(lèi)
@Configuration
public class OpenFeignLogConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
3.配置文件
logging:
level:
*[feign日志以什么級(jí)別監(jiān)控哪個(gè)接口]:
com.ik.ikong.service.FeignClientService: debug
六、高階配置
七、Feign與Springboot版本
版本對(duì)照關(guān)系如下,詳情也可以點(diǎn)擊這里的鏈接在官網(wǎng)查看最新版本兼容關(guān)系
八、技術(shù)問(wèn)題
在調(diào)用接口時(shí)候idea報(bào)錯(cuò)
no suitable HttpMessageConverter found for response type
解決過(guò)程
1.放開(kāi)日志
@Configuration
public class FeignConfiguration {
@Bean
public feign.Logger logger() {
return new Slf4jLogger();
}
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
logging:
level:
feign.Logger: debug
日志詳細(xì)
從放開(kāi)的日志來(lái)看,接口返回的head里:content-type: text/html; charset=UTF-8
雖然我在接口簽名上增加了produces、consumes
@PostMapping(value = "/xxxxx/your/api", produces = "application/json;charset=utf-8", consumes = "application/json;charset=utf-8")
可以看到毫無(wú)效果,仍舊異常:no suitable HttpMessageConverter found for response type
猜想原生的http返回值只對(duì)application/json類(lèi)型做了泛型的反序列化處理,是對(duì)接口返回的content-type: text/html;并不能進(jìn)行json反序列化處理,遂增加該類(lèi)型的json反序列化處理;
@Bean
public HttpMessageConverters customConverters() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML,MediaType.APPLICATION_JSON));
return new HttpMessageConverters(false, Arrays.asList(mappingJackson2HttpMessageConverter, new StringHttpMessageConverter()));
}
重點(diǎn)在mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML,MediaType.APPLICATION_JSON));
最后成功完成增加content-type: text/html的json反序列化;
參考:Spring Cloud文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-767092.html
Feign實(shí)現(xiàn)服務(wù)間的Http調(diào)用。_feign http調(diào)用_半命i的博客-CSDN博客文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-767092.html
到了這里,關(guān)于互聯(lián)網(wǎng)大廠(chǎng)技術(shù)-HTTP請(qǐng)求-Springboot整合Feign更優(yōu)雅地實(shí)現(xiàn)Http服務(wù)調(diào)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!