【Fegin技術專題】「原生態」打開Fegin之RPC技術的開端,你會使用原生態的Fegin嗎?(高級用法)
theme: smartblue
本文已參與「掘力星計劃」,贏取創作大禮包,挑戰創作激勵金。
對於Httpclient請求機制進行設置操作處理。
@Body請求體模板
@Body註解申明一個請求體模板,模板中可以帶有參數,與方法中@Param註解申明的參數相匹配,使用方法如下:
java
interface LoginClient {
@RequestLine("POST /")
@Headers("Content-Type: application/json")
// json curly braces must be escaped!
// 這裏JSON格式需要的花括號居然需要轉碼,有點蛋疼了。
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
void login(@Param("user_name") String user, @Param("password") String password);
}
...
client.login("denominator", "secret");
// {"user_name": "denominator", "password": "secret"}
Headers請求頭
Feign支持給請求的api設置或者請求的客户端設置請求頭,如下:
給API設置請求頭
使用 @Headers 設置靜態請求頭
java
// 給BaseApi中的所有方法設置Accept請求頭
@Headers("Accept: application/json")
interface BaseApi<V> {
// 單獨給put方法設置Content-Type請求頭
@Headers("Content-Type: application/json")
@RequestLine("PUT /api/{key}")
void put(@Param("key") String, V value);
}
設置動態值的請求頭
java
@RequestLine("POST /")
@Headers("X-Ping: {token}")
void post(@Param("token") String token);
設置key和value都是動態的請求頭
調用時動態確定使用不同的請求頭
可以使用 @HeaderMap 註解,如下:
java
// @HeaderMap 註解設置的請求頭優先於其他方式設置的
@RequestLine("POST /")
void post(@HeaderMap Map<String, Object> headerMap);
給Target設置請求頭
有時我們需要在一個API實現中根據不同的endpoint來傳入不同的Header,這個時候我們可以使用自定義的RequestInterceptor 或 Target來實現.
通過自定義的 RequestInterceptor 來實現請查看 Request Interceptors
下面是一個通過自定義Target來實現給每個Target設置安全校驗信息Header的例子:
java
static class DynamicAuthTokenTarget<T> implements Target<T> {
public DynamicAuthTokenTarget(Class<T> clazz,
UrlAndTokenProvider provider,
ThreadLocal<String> requestIdProvider);
...
@Override
public Request apply(RequestTemplate input) {
TokenIdAndPublicURL urlAndToken = provider.get();
if (input.url().indexOf("http") != 0) {
input.insert(0, urlAndToken.publicURL);
}
input.header("X-Auth-Token", urlAndToken.tokenId);
input.header("X-Request-ID", requestIdProvider.get());
return input.request();
}
}
...
Bank bank = Feign.builder()
.target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider));
-
這種方法的實現依賴於給Feign 客户端設置的自定義的RequestInterceptor 或 Target。可以被用來給一個客户端的所有api請求設置請求頭。比如説可是被用來在header中設置身份校驗信息。這些方法是在線程執行api請求的時候才會執行,所以是允許在運行時根據上下文來動態設置header的。
-
比如説可以根據線程本地存儲(thread-local storage)來為不同的線程設置不同的請求頭。
Base APIS
有些請求中的一些方法是通用的,但是可能會有不同的參數類型或者返回類型,這個時候可以這麼用:
```java
// 通用API
interface BaseAPI {
@RequestLine("GET /health")
String health();
@RequestLine("GET /all")
List
```
Logging
你可以通過設置一個 Logger 來記錄http消息,如下:
java
GitHub github = Feign.builder()
decoder(new GsonDecoder())
.logger(new Logger.JavaLogger().appendToFile("logs/http.log"))
.logLevel(Logger.Level.FULL)
.target(GitHub.class, http://api.github.com);
Request Interceptors
當你希望修改所有的的請求的時候,你可以使用Request Interceptors。比如説,你作為一箇中介,你可能需要為每個請求設置 X-Forwarded-For
java
static class ForwardedForInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) {
template.header("X-Forwarded-For", "origin.host.com");
}
}
...
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new ForwardedForInterceptor())
.target(Bank.class, http://api.examplebank.com);
或者,你可能需要實現Basic Auth,這裏有一個內置的基礎校驗攔截器
java
BasicAuthRequestInterceptor
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new BasicAuthRequestInterceptor(username, password))
.target(Bank.class, http://api.examplebank.com);
@Param Expansion
@Param 註解給模板中的參數設值的時候,默認的是使用的對象的 toString() 方法的值,通過聲明 自定義的Param.Expander,用户可以控制其行為,比如説格式化 Date 類型的值:
java
// 通過設置 @Param 的 expander 為 DateToMillis.class 可以定義Date類型的值
@RequestLine("GET /?since={date}")
Result list(@Param(value = "date", expander = DateToMillis.class) Date date);
Dynamic Query Parameters
動態查詢參數支持,通過使用 @QueryMap 可以允許動態傳入請求參數,如下:
java
@RequestLine("GET /find")
V find(@QueryMap Map<String, Object> queryMap);
自定義註解掃描動態生成客户端
原生Feign只能一次解析一個接口,生成對應的請求代理對象,如果一個包裏有多個調用接口就要多次解析非常麻煩。
擴展BeanFactoryPostProcessor接口、
自定義註解:在掃描接口的過程中,可以通過一個自定義註解,來區分Feign接口並且指定調用的服務Url
實現擴展容器
java
@Component
public class FeignClientRegister implements BeanFactoryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
List<String> classes = scan(scanPath);
if(classes==null){
return ;
}
Feign.Builder builder = getFeignBuilder();
if(classes.size()>0){
for (String claz : classes) {
Class<?> targetClass = null;
try {
targetClass = Class.forName(claz);
String url=targetClass.getAnnotation(FeignApi.class).serviceUrl();
if(url.indexOf("http://")!=0){
url="http://"+url;
}
Object target = builder.target(targetClass, url);
beanFactory.registerSingleton(targetClass.getName(), target);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
public Feign.Builder getFeignBuilder(){
Feign.Builder builder = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.options(new Request.Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3));
return builder;
}
public List<String> scan(String path){
ScanResult result = new FastClasspathScanner(path).matchClassesWithAnnotation(FeignApi.class, (Class<?> aClass) -> {
}).scan();
if(result!=null){
return result.getNamesOfAllInterfaceClasses();
}
return null;
}
}
- 完整秒殺架構的設計到技術關鍵點的“情報信息”
- 獨一無二的「MySQL調優金字塔」相信也許你擁有了它,你就很可能擁有了全世界。
- 【MySQL技術之旅】(5)該換換你的數據庫版本了,讓我們一同迎接8.0的到來哦!(初探篇)
- ☕【Java技術指南】「Java8編程專題」讓你真正會用對Java新版日期時間API編程指南
- 【Fegin技術專題】「原生態」打開Fegin之RPC技術的開端,你會使用原生態的Fegin嗎?(高級用法)
- 【優化技術專題】「線程間的高性能消息框架」終極關注Disruptor的核心源碼和Java8的@Contended偽共享指南
- 【優化技術專題】「線程間的高性能消息框架」再次細節領略Disruptor的底層原理和優勢分析
- 【Zookeeper核心原理】Paxos協議的原理和實際運行中的應用流程分析
- ☕【Java技術指南】「JPA編程專題」讓你不再對JPA技術中的“持久化型註解”感到陌生了!
- Java技術開發專題系列之【Guava RateLimiter】針對於限流器的入門到精通(含源碼分析介紹)
- ☕【Java技術指南】「JPA編程專題」讓你不再對JPA技術中的“持久化型註解”感到陌生了!
- 【Eureka技術指南】「SpringCloud」從源碼層面讓你認識Eureka工作流程和運作機制(下)
- MySQL技術專題(6)這也許是你的知識盲區-MySQL主從架構以及[半同步機制]
- 優化技術專題-線程間的高性能消息框架-深入淺出Disruptor的使用和原理
- ☕【Java技術指南】「併發編程專題」Fork/Join框架基本使用和原理探究(原理篇)
- ☕【Java技術指南】「併發編程專題」Guava RateLimiter針對於限流器的入門到精通(含源碼分析介紹)
- 【優化技術專題】「温故而知新」基於Quartz系列的任務調度框架的動態化任務實現分析
- ☕【Java技術指南】「併發編程專題」Guava RateLimiter針對於限流器的入門到精通(含實戰和原理分析)
- 【MySQL技術之旅】(4)這也許是你的知識盲區-[MySQL主從架構]之半同步機制
- ☕【Java技術指南】「併發編程專題」CompletionService框架基本使用和原理探究(基礎篇)