Open?Feign之非SpringCloud方式使用示例
前言
網(wǎng)上對于spring-cloud-starter-openfeign的使用有非常多的說明,此處不再贅述。
機(jī)緣巧合之下,筆者遇到希望輕量級使用Open Feign的場景,即項目中并未使用SpringCloud框架、注冊中心等服務(wù)發(fā)現(xiàn)組件,而只是想簡單的做遠(yuǎn)程http請求調(diào)用來解耦微-微服務(wù)。
OpenFeign是什么
Feign 是netflix提供的開源http client庫,目前已經(jīng)停止維護(hù)。
隨后,Spring Cloud官方提供了Open Feign,對Feign做了如下增強(qiáng):
- 支持SpringMVC注解
- 整合Ribbon、Nacos等
它與Apache HttpClient不同,它可以像調(diào)用本地方法一樣進(jìn)行遠(yuǎn)程方法調(diào)用;對,它也是一個RPC框架。
原生注解(不推薦)
@RequestLine("POST /postJson") @Headers("Content-Type: application/json") OrderDto postJson (OrderDto dto); @RequestLine("POST /postForm") @Headers("Content-Type: application/x-www-form-urlencoded") OrderDto postForm (@Param("id") String id, @Param("name") String name); @RequestLine("GET /get?id={id}&name={name}") String get(@Param("id") String id, @Param("name") String name); @RequestLine("GET /get") String getByMap(@QueryMap Map<String, Object> param); @RequestLine("GET /getById/{id}") String getById(@Param("id") String id);
原生注解@RequestLine有額外的理解成本,我們一般不會使用,上面僅做示例。
注意:
1、參數(shù)@Param注解需要與@RequestLine中的{xxx} 對應(yīng)
2、表單方式需要依賴feign-form
spring注解最佳實踐(推薦)
從10.5.0版本開始提供了feign-spring4,來適配spring注解。
使用spring注解需要將contract契約設(shè)置為SpringContract。
1、引入依賴
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>11.6</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-spring4</artifactId> <version>11.6</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-jackson</artifactId> <version>11.6</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>11.6</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.8.0</version> </dependency>
2、定義RPC接口
public interface HelloFacade { // spring注解用法 @PostMapping(value = "/postJson", consumes = "application/json") OrderDto postJson (@RequestBody OrderDto dto); @PostMapping(value = "/postForm", consumes = "application/x-www-form-urlencoded") OrderDto postForm (OrderDto dto); @GetMapping("/get") OrderDto get(@RequestParam("id") Long id, @RequestParam("name") String name); @GetMapping("/getByMap") OrderDto getByMap(@RequestParam("param") Map<String, Object> param); @GetMapping("/getById/{id}") OrderDto getById(@PathVariable("id") Long id); }
3、自定義Json解碼器
public class MyJacksonDecoder extends JacksonDecoder { @Override public Object decode(Response response, Type type) throws IOException { if (response.body() == null) { return null; } if (type == String.class) { return StreamUtils.copyToString(response.body().asInputStream(), StandardCharsets.UTF_8); } return super.decode(response, type); } }
默認(rèn)的JacksonDecoder直接拿著字符串做json反序列化,而當(dāng)我們存在接口返回值是String時,就會格式化報錯。
所以我們集成JacksonDecoder,特殊處理一下String類型即可(String類型不需要經(jīng)過json格式化)
4、配置HttpClient線程池
public static CloseableHttpClient getHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslSf = new SSLConnectionSocketFactory(builder.build()); // 配置同時支持 HTTP 和 HTTPS Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslSf).build(); // 初始化連接管理器 PoolingHttpClientConnectionManager poolConnManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); // 同時最多連接數(shù)(不設(shè)置默認(rèn)20) poolConnManager.setMaxTotal(20); // 設(shè)置最大路由(不設(shè)置默認(rèn)2) poolConnManager.setDefaultMaxPerRoute(10); // 此處解釋下MaxtTotal和DefaultMaxPerRoute的區(qū)別: // 1、MaxtTotal是整個池子的大?。? // 2、DefaultMaxPerRoute是根據(jù)連接到的主機(jī)對MaxTotal的一個細(xì)分;比如: // MaxtTotal=400 DefaultMaxPerRoute=200 // 而只連接到http://www.abc.com時,到這個主機(jī)的并發(fā)最多只有200;而不是400; // 連接到http://www.bac.com 和 http://www.ccd.com時,到每個主機(jī)的并發(fā)最多只有200;即加起來是400(但不能超過400); // 所以起作用的設(shè)置是DefaultMaxPerRoute // 初始化httpClient RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) .setConnectionRequestTimeout(2000) .setSocketTimeout(10000).build(); return HttpClients.custom() // 設(shè)置連接池管理 .setConnectionManager(poolConnManager) .setDefaultRequestConfig(config) // 過期連接關(guān)閉 .evictIdleConnections(60, TimeUnit.SECONDS) .setConnectionTimeToLive(600, TimeUnit.SECONDS) // 設(shè)置重試次數(shù) .setRetryHandler(new DefaultHttpRequestRetryHandler(1, false)).build(); }
OpenFeign默認(rèn)Http客戶端是HttpURLConnection(JDK自帶的Http工具),該工具不能配置連接池,生產(chǎn)中使用時性能較差,故我們配置自己的Apache HttpClient連接池。(當(dāng)然Open Feign也有OkHttp的適配)
5、編寫調(diào)用入口
public static void main(String[] args) throws Exception{ HelloFacade target = Feign.builder() // spring注解 .contract(new SpringContract()) .encoder(new FormEncoder(new JacksonEncoder())) .decoder(new MyJacksonDecoder()) .client(new ApacheHttpClient(getHttpClient())) .logLevel(Logger.Level.BASIC) .target(HelloFacade.class, "http://localhost:8080"); String result = target.testStr("hello world"); System.out.println(result); }
通過Feign.builder 鏈?zhǔn)匠跏蓟?,設(shè)置Spring注解契約、json編解碼器、http客戶端工具、日志級別、需要動態(tài)代理的接口、以及目標(biāo)服務(wù)的地址。
6、RequestInterceptor攔截器擴(kuò)展
真實業(yè)務(wù)場景下,我們可能需要統(tǒng)一添加header經(jīng)過鑒權(quán)邏輯,或者根據(jù)環(huán)境進(jìn)行轉(zhuǎn)發(fā)不同的目標(biāo)地址,而不是寫死。
那么我們就可以實現(xiàn)OpenFeign提供的RequestInterceptor攔截器接口。
@Component public class TargetUrlRequestInterceptor implements RequestInterceptor { @Value("${feign.target.url:http://localhost:7777}") private String targetUrl; @Override public void apply(RequestTemplate template) { // 可以添加自定義header template.header("customHeader", "123"); // 可以改變初始化時的目標(biāo)地址 template.target(targetUrl); } }
Open Feign架構(gòu)設(shè)計圖
此處貼一張網(wǎng)上找的架構(gòu)設(shè)計圖,供參考學(xué)習(xí)
以上就是Open Feign之非SpringCloud方式使用示例的詳細(xì)內(nèi)容,更多關(guān)于Open Feign非SpringCloud的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot項目打war包docker包找不到resource下靜態(tài)資源的解決方案
今天小編就為大家分享一篇關(guān)于Springboot項目打war包docker包找不到resource下靜態(tài)資源的解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03IntelliJ IDEA maven 構(gòu)建簡單springmvc項目(圖文教程)
在工作當(dāng)中,我們有時需要創(chuàng)建一個全新的工程,而基于spring-mvc web的工程較為常見,這篇文章主要介紹了IntelliJ IDEA maven 構(gòu)建簡單springmvc項目(圖文教程),感興趣的小伙伴們可以參考一下2018-05-05關(guān)于maven配置項目一直提示程序包不存在以及scope的坑
這篇文章主要介紹了關(guān)于maven配置項目一直提示程序包不存在以及scope的坑,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11關(guān)于Object中equals方法和hashCode方法判斷的分析
今天小編就為大家分享一篇關(guān)于關(guān)于Object中equals方法和hashCode方法判斷的分析,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01分別在Groovy和Java中創(chuàng)建并初始化映射的不同分析
這篇文章主要為大家介紹了分別在Groovy和Java中創(chuàng)建并初始化映射的不同分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03SpringBoot使用EmbeddedDatabaseBuilder進(jìn)行數(shù)據(jù)庫集成測試
在開發(fā)SpringBoot應(yīng)用程序時,我們通常需要與數(shù)據(jù)庫進(jìn)行交互,為了確保我們的應(yīng)用程序在生產(chǎn)環(huán)境中可以正常工作,我們需要進(jìn)行數(shù)據(jù)庫集成測試,在本文中,我們將介紹如何使用 SpringBoot 中的 EmbeddedDatabaseBuilder 來進(jìn)行數(shù)據(jù)庫集成測試2023-07-07