[享学Feign] 九、Feign + OkHttp和Feign + Apache HttpClient哪个更香?

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-okhttpfeign-httpclientfeign-googlehttpclient。本文将会讨论前两者


OkHttp

它的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请求的发送,不同之处在于进行了两次数据转换:

  1. Request之前的转换
  2. Response之间的转换

其中,需要特别特别注意的是:请务必确保每次请求都是线程安全的feign.Client接口的Javadoc也特别强调了这一点~


Apache HttpClient

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);
  }

}

GoogleHttpClient

它基于的是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才更优秀、更具竞争力。

分隔线

声明

原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭。你也可【左边扫码/或加wx:fsx641385712】邀请你加入我的 Java高工、架构师 系列群大家庭学习和交流。
往期精选

发布了377 篇原创文章 · 获赞 572 · 访问量 51万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie

分享到微信朋友圈

×

扫一扫,手机浏览