纠错:Feign 没用 短链接

Feign 默认不是 短链接

疯狂创客圈 Java 高并发【 亿级流量聊天室实战】实战系列 【博客园总入口html


疯狂创客圈(笔者尼恩建立的高并发研习社群)Springcloud 高并发系列文章,将为你们介绍三个版本的 高并发秒杀:java

1、版本1 :springcloud + zookeeper 秒杀面试

2、版本2 :springcloud + redis 分布式锁秒杀redis

3、版本3 :springcloud + Nginx + Lua 高性能版本秒杀spring

以及有关Springcloud 几篇核心、重要的文章缓存

1、Springcloud 配置, 史上最全 一文全懂tomcat

2、Springcloud 中 SpringBoot 配置全集 , 收藏版并发

3、Feign Ribbon Hystrix 三者关系 , 史上最全 深度解析socket

4、SpringCloud gateway 详解 , 史上最全分布式

5、图解:tomcat的maxConnections、maxThreads、acceptCount | 秒懂

前言

网上不少文章都说,Feign 默认采用短链接进行远程调用,其实,这种结论是不对的。为何呢? 下面细致的为你们来解读。

Feign中默认状况下的长链接

Feign中,默认状况下,使用的是JDK1.8中的 HttpURLConnection 基础链接类。该类的内部,使用了JDK1.8自带的 HttpClient 请求客户端类去负责完成底层的socket流操做。另外,JDK1.8还提供了一个简单的长链接缓冲类 KeepAliveCache,实现HttpClient 请求客户端类的缓存和复用。

三个类的所处位置为:HttpURLConnection 类处于 java.net 包中,而 HttpClient 类和KeepAliveCache类,则处于sun.net.www.http 包中。

HttpURLConnection、HttpClient、KeepAliveCache三个类的简单关系为:

每一个HTTP请求都是一个HttpURLConnection实例,每一个请求都会有一个 HttpClient 客户端实例,一个HttpClient 实例都持有一个TCP socket 长链接。若是 HttpClient 实例能够复用,则暂存在KeepAliveCache 缓存实例中。HttpURLConnection 会优先从缓存中取得合适的HttpClient 客户端,若是缓存中没有,HttpURLConnection 才会选择去建立新的HttpClient 实例。

经过三者之间的关系,能够看出: HttpClient 实例的复用,就是底层 TCP socket 长链接的复用。

JDK1.8 的 HttpClient 实例的复用的流程

下面,经过单步跟踪的方式,说一下HttpClient 实例的复用大体流程。

(1)首先,从默认的 feign.Client.Default 客户端的 execute 方法开始。

execute 方法首先会调用 convertAndSend(connection, request) 方法,打开一个URL链接实例,也便是一个 HttpURLConnection 类型的实例。而后,在convertResponse 方法中,开始获取响应码。这个时候,请求的整个处理过程,才真正开始。

在这里插入图片描述

(2) 经过单步跟踪发现,connection.getResponseCode() 语句执行过程当中, 会经过调用HttpClient.New(..) 获取一个可用的 HttpClient 客户端实例。

HttpClient.New(..)是一个静态方法,大体的逻辑:它会调用 KeepAliveCache 类型的静态成员 kac 的get方法,去首先获取缓存中的HttpClient 客户端实例。

在这里插入图片描述

KeepAliveCache实例的 get方法,会主要以请求的 url 值做为 key,去缓存中查找是否有绑定的 HttpClient 实例,若是有的话直接拿过来用。

在这里插入图片描述

(3) 若是KeepAliveCache 缓存实例中没有,则调用HttpClient的构造器,新建一个HttpClient 对象,这个构造方法的最后一行,会调用了openServer() 方法,这个时候才会去真正的创建TCP链接。

在这里插入图片描述

(4) 至此,HttpURLConnection 实例的getResponseCode() 方法,终于拿到了内部的 HttpClient 链接,这个时候能够向 SERVER 端写请求数据了,这个时候会调用 writeRequests 方法。

HttpURLConnection 实例的writeRequests方法,首先会判断 httpClient.isKeepAlive 的值,该值默认是true,因此在请求上加上了 Connection:keep-alive 请求头 。

在这里插入图片描述

(5)writeRequests 方法写数据完成以后,会调用HttpClient.parseHTTP(..)方法,去解析服务端响应的数据,包括服务的响应头。

在parseHTTP(..)方法中,若是响应头中包含了Connection:keep-alive,并设置了Keep-Alive 头,好比含有如下内容:“Keep-Alive:timeout=xx,max=xxx” ,其中 timeout 表示服务端的‘空闲’超时时间,max表示长链接最多处理多少个请求。则这两个值,将覆盖掉 httpClient对象的keepAliveTimeout 和 keepAliveConnections 属性的值。

(6) parseHTTP(..)方法读取完数据以后,最终会调用到httpClient.finished方法,将当前httpClient对象,加入到缓存中,这个地方是实现 TCP 链接复用的关键。
在这里插入图片描述

(7)HttpClient的 putInKeepAliveCache方法,主要以请求的 url 值做为 key (由于这里的第二个参数老是写死为 null ),以当前 HttpClient 实例为 value,放入到KeepAliveCache 类型的静态成员 kac 缓存中,以便后面进行复用 。

在这里插入图片描述

经过以上的七步,JDK1.8 实现了HttpURLConnection 的长链接。

这里,有一个问题:什么Feign调用会默认在请求头中加上Connection:keep-alive?

缘由是这样的,在 sun.net.www.http.HttpClient 类的静态初始化代码部分,包括了 keepAliveProp 静态属性的初始化。keepAliveProp 静态属性的值,是决定HttpClient 客户端实例是否复用的关键之一,若是keepAliveProp 静态属性值为false,则不管如何都不会进行链接复用。sun.net.www.http.HttpClient 类的静态初始化代码节选以下:

static {
        String var0 = (String)AccessController.doPrivileged(new GetPropertyAction("http.keepAlive"));
        //…省略不相干代码
        if (var0 != null) {
            keepAliveProp = Boolean.valueOf(var0);
        } else {
            keepAliveProp = true;  //默认值为true
        }
         //…省略不相干代码
}

能够看到,首先取得一个系统配置项 http.keepAlive 的值,若是该配置项的值没有作专门的设置或者修改,则sun.net.www.http.HttpClient 静态属性 keepAliveProp 的值,默认被赋值为true。

经过阅读源码能够知道,静态属性 keepAliveProp 的值,是决定HttpClient 的实例对象是否放入长链接缓冲池 KeepAliveCache 的一个重要关键属性值。也就是说,这个属性为true,则 HttpClient 实例对象具有复用的可能,不然,HttpClient 实例对象不能被复用。

至此,关于JDK1.8 实现如何实现长链接,也就介绍完了。不过,细心的读者可能会发现,HttpURLConnection 内部的长链接复用,和URL有关:只有在URL字符串相同的状况下,才能进行复用。这就有一个问题,若是URL中带有变量值,好比 /order/1/detail、/order/2/detail ,则不一样的参数,不能进行HttpClient 实例对象的复用。

JDK默认的HttpClient 实例对象的复用的问题

和ApacheHttpClient 链接复用相比,JDK默认的HttpClient 实例对象的复用,有如下问题:

(1)JDK默认的 HttpClient 实例对象复用的粒度过小,只有URL相同的状况下,才能进行链接复用。而 ApacheHttpClient 链接复用的粒度则大不少,同路由的链接,就能够复用。

(2)在URL字符串变化比较大的场景下,JDK默认的 HttpClient 实例对象的内部链接,会保持一段时间才被释放,会占用系统的链接资源,更加不利于高并发。

因此,从以上两点出发,因为不能相同保证URL的请求数据巨大,因此不建议使用JDK默认的HttpClient 实例对象。建议在Feign中,使用ApacheHttpClient 链接池进行链接的复用

最后,介绍一下疯狂创客圈:疯狂创客圈,一个Java 高并发研习社群博客园 总入口

疯狂创客圈,倾力推出:面试必备 + 面试必备 + 面试必备 的基础原理+实战 书籍 《Netty Zookeeper Redis 高并发实战

img


疯狂创客圈 Java 死磕系列

  • Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战

  • Netty 源码、原理、JAVA NIO 原理
  • Java 面试题 一网打尽
  • 疯狂创客圈 【 博客园 总入口 】


ty) 聊天程序【 亿级流量】实战 开源项目实战**

相关文章
相关标签/搜索