前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[享学Feign] 九、Feign + OkHttp和Feign + Apache HttpClient哪个更香?

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

作者头像
YourBatman
发布2020-02-21 16:38:35
5.8K0
发布2020-02-21 16:38:35
举报
文章被收录于专栏:BAT的乌托邦

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如下:

代码语言:javascript
复制
<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即可


使用示例
代码语言:javascript
复制
public interface DemoClient {

    @RequestLine("GET /feign/demo1?name={name}")
    String getDemo1(@Param("name") String name);
}

构建Feign时,指定使用OkHttpClient

代码语言:javascript
复制
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里,直接使用即可,形如这样:

代码语言:javascript
复制
// 项目内定制好的共用的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接口的实现。

代码语言:javascript
复制
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如下:

代码语言:javascript
复制
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>${feign.version}</version>
</dependency>

它是基于Apache HttpClient实现的,携带的HttpClient版本号是:4.5.3


使用示例

使用方式几乎同上。

代码语言:javascript
复制
Feign.builder().client(new ApacheHttpClient()) ... 

你可以可以使用你定义好的HC:

代码语言:javascript
复制
Feign.builder().client(new ApacheHttpClient(myApacheHttpClient())) ... 

源码解析

同样的,feign-httpclient这个jar有且仅有一个类:ApacheHttpClient

代码语言:javascript
复制
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才更优秀、更具竞争力。

分隔线
分隔线

声明

原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • OkHttp
      • 使用示例
      • 源码解析
    • Apache HttpClient
      • 使用示例
      • 源码解析
    • GoogleHttpClient
      • 哪个更香?
      • 总结
        • 声明
        相关产品与服务
        云数据库 Redis
        腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档