Redis作者说到:“灵活性被过分高估–>约束才是解放”。
代码下载地址:https://github.com/f641385712/feign-learning
前八篇文章介绍完了feign-core核心内容,从本篇开始将介绍它的“其它模块”。其实核心模块可以独立的work,但是不免它的能力偏弱,比如只能编码字符串类型、只能解码字符串类型,默认使用java.net.HttpURLConnection
作为HC…
本篇将介绍它的第一个模块:Client相关模块。我们知道,流行的开源Http库的性能均远高于JDK源生的HttpURLConnection
,因此实际生产中肯定是用的三方库来发送Http请求。
Feign它提供了feign.Client
抽象来发送Http请求,因此使得它拥有良好的扩展性,而恰好Feign的子模块里亦提供了对OkHttp
以及Apache HttpClient
的整合,本文将教你如何把Feign切换为第三方HC以提高性能。
我们知道Feign在默认情况下,它发送Http请求使用的是JDK源生的HttpURLConnection
。而在实际生产环境下,直接使用它是100%不可取的,这就需要我们使用更加高效的HC。
Feign的模块中有三个关于HC的子模块:feign-okhttp
、feign-httpclient
、feign-googlehttpclient
。本文将会讨论前两者
它的GAV如下:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>${feign.version}</version>
</dependency>
"携带"的okhttp版本号是:3.6.0
。(若把Feign调整到最新版本10.7.4
,那么它携带的okhttp版本号也就是最新的3.14.6
的了)
说明:okhttp虽然目前最新版本是4.x版本的,关于区别你可以简单粗暴的理解:前者是用kotlin改写了,后者还是用Java写的,其它的并无什么变化。 所以,在Server端使用okhttp,请务必使用3.x版本~移动端可酌情使用4.x版本
通过前八篇文章对Feign核心内容的学习,知道Feign最终是通过它的feign.Client
这个API去发送远程请求的,而feign.Client
是可以在构建的时候由使用者自定义指定的。有了以上理论的支撑,若想切换最终发送Http请求的HC,仅需在构建时使用自己的feign.Client
即可。
public interface DemoClient {
@RequestLine("GET /feign/demo1?name={name}")
String getDemo1(@Param("name") String name);
}
构建Feign时,指定使用OkHttpClient
:
public static void main(String[] args) {
DemoClient client = Feign.builder()
.client(new OkHttpClient()) // 显示指定使用OkHttpClient
.target(DemoClient.class, "http://localhost:8080");
String result = client.getDemo1("YourBatman");
System.out.println(result);
}
一切正常work。附如下截图,以证明确实是okhttp在生效:
当然喽,如果你已经有现成的定制好的okhttpClient里,直接使用即可,形如这样:
// 项目内定制好的共用的OkHttpClient
// 注意:它和feign.okhttp.OkHttpClient同名哦
private static okhttp3.OkHttpClient myOkHttpClient(){
okhttp3.OkHttpClient hc = new okhttp3.OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.followRedirects(true)
.build();
return hc;
}
Feign.builder().client(new OkHttpClient(myOkHttpClient()))
feign-okhttp
这个jar包内,有且仅有一个类:feign.okhttp.OkHttpClient
,它是对feign.Client
接口的实现。
public final class OkHttpClient implements Client {
private final okhttp3.OkHttpClient delegate;
// 空构造器:使用默认的OkHttpClient配置
public OkHttpClient() {
this(new okhttp3.OkHttpClient());
}
// 自己指定Client,比如用你项目里配置好的、公用的HC
public OkHttpClient(okhttp3.OkHttpClient delegate) {
this.delegate = delegate;
}
// 静态方法:把feign.Request转换为okhttp3.Request
static Request toOkHttpRequest(feign.Request input) {
...
// 什么时候发送时带Body发送呢?
// 方法是POST/PUT/PATCH时body才生效,其它时候body直接忽略掉
// 这是和JDK源生Client的区别哦
boolean isMethodWithBody = HttpMethod.POST == input.httpMethod()
|| HttpMethod.PUT == input.httpMethod()
|| HttpMethod.PATCH == input.httpMethod();
...
}
// 静态方法:把okhttp3.Response转为feign.Response
static feign.Response toFeignResponse(Response response, feign.Request request) {
...
}
// 把okhttp3.ResponseBody转为feign.Response.Body
static feign.Response.Body toBody(final ResponseBody input) throws IOException {
...
}
// 执行:发送Http请求,交给OkHttpClient带来来发送
@Override
public feign.Response execute(feign.Request input, feign.Request.Options options) throws IOException {
// 此处采用局部变量,而非直接在delegate身上操作,是为了保证线程安全
okhttp3.OkHttpClient requestScoped;
// 注意这个判断:只有当你传进来的Options值,和当前的delegate不相等,我才给你重新构建一个实例
// 若相等就没必要构建了嘛
// 说明:请务必使用新构建的方式,以保证线程安全
if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
|| delegate.readTimeoutMillis() != options.readTimeoutMillis()) {
// 重新构建,使用Options传进来的值
requestScoped = delegate.newBuilder()
.connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
.readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
.followRedirects(options.isFollowRedirects())
.build();
} else {
requestScoped = delegate;
}
// 转换为okHttp的请求对象
Request request = toOkHttpRequest(input);
// 发送http远程请求
Response response = requestScoped.newCall(request).execute();
// 把okhttp的respone转换为Feign自己的
return toFeignResponse(response, input).toBuilder().request(input).build();
}
}
这个逻辑不难,其实就一普通的Http请求的发送,不同之处在于进行了两次数据转换:
其中,需要特别特别注意的是:请务必确保每次请求都是线程安全的。feign.Client
接口的Javadoc也特别强调了这一点~
GAV如下:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign.version}</version>
</dependency>
它是基于Apache HttpClient
实现的,携带的HttpClient
版本号是:4.5.3
。
使用方式几乎同上。
Feign.builder().client(new ApacheHttpClient()) ...
你可以可以使用你定义好的HC:
Feign.builder().client(new ApacheHttpClient(myApacheHttpClient())) ...
同样的,feign-httpclient
这个jar有且仅有一个类:ApacheHttpClient
public final class ApacheHttpClient implements Client {
private final HttpClient client;
public ApacheHttpClient() {
this(HttpClientBuilder.create().build());
}
public ApacheHttpClient(HttpClient client) {
this.client = client;
}
...
@Override
public Response execute(Request request, Request.Options options) throws IOException {
HttpUriRequest httpUriRequest;
try {
// 请注意:HttpUriRequest实例,每次都是通过`org.apache.http.client.methods.RequestBuilder`构建出来的一个新实例
// 因此确保了线程安全性
httpUriRequest = toHttpUriRequest(request, options);
} catch (URISyntaxException e) {
throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
}
// 发送http请求,然后转换response
HttpResponse httpResponse = client.execute(httpUriRequest);
return toFeignResponse(httpResponse, request);
}
}
它基于的是Google的HC客户端google-http-client
实现的。比如典型API是com.google.api.client.http.HttpTransport
,该jar包同样也只有一个类:GoogleHttpClient
。
因这个客户端国内使用实在太少,因此本文就此略过。
–
Apache HttpClient
是老牌HC,具有很多优秀的“品质”,值得信赖;而OkHttp
作为后起之秀,具有更加优越的性能表现,大有干掉老牌HC的势头。
总的来说:个人倾向于推荐使用Feign + OkHttp
的组合应用在你的生产环境中。
本文介绍了Feign它首个子模块:关于Client的子模块。因为生产环境是,必定会使用OkHttp
或者Apache HttpClient
作为实际的HC,所以本篇文章应该能对你实际工作中会有所帮助。
从此篇开始,介绍的均是Feign的其它子模块,它可以理解为对feign-core核心内容的扩展,使得它提供的能力越来越完善,方便在生产上提供一站式的解决方案,这样Feign才更优秀、更具竞争力。
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。