feign 使用tcp连接池

feign实现

跟踪程序断点发现feign每次调用时,均通过(HttpURLConnection) new URL(request.url()).openConnection()建立新的连接

feign使用连接池

pom配置

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>

application配置

feign:
  httpclient:
    enabled: true

测试


启用上述配置后,此处的client使用的不再是默认client而是ApacheHttpClient

发现此处使用的是AbstractConnPool,同zuul的实现

常规压测

实现

  • feign
@FeignClient(name = "baiduFeign", url = "https://www.baidu.com")
public interface BaiduFeign {

    @GetMapping
    String baidu();
}
  • controller
@RestController
@RequestMapping("/baidu")
public class BaiduController {

    @Autowired
    private BaiduFeign baiduFeign;

    @GetMapping
    public String baidu() {
        return baiduFeign.baidu();
    }
}
  • 分别在使用feign-httpclient与不使用的情况下通过wrk压测
 wrk -t1 -c1  -d 1000s -H "Authorization: 0cb99a92f9ad4f998d09c515c4299a86"  https://p-api-test.taoche.cn/v1/business-application/baidu

压测分析

发现使用new URL(request.url()).openConnection()AbstractConnPool两种方式的请求时间结果并没有明显变化


并且发现在使用new URL(request.url()).openConnection()方式时连接的端口一直没有变化

原因为HttpURLConnction在Connection: close模式时使用完成后调用关闭InputStream会自动关闭连接;当使用Connection: Keep-Alive模式时,关闭InputStream后,并不会断开底层Socket连接,当需要连接到同一服务器地址时,可以利用该Socket,这时如果要断开连接,可调用connection.disconnect()

由于当前http默认均为长连接,故使用上述两种方式均会复用tcp连接,所以接口性能无明显变化

降低调用频次

实现

  • feign 同上
  • controller 同上
  • 分别在使用feign-httpclient与不使用的情况下通过以下test类压测
@Test
public void test() throws Exception {
    for (int i = 0; i < 1000; i++) {
        String resp = testFeign.test("34bde626faf04965b1023d334d9698fe");
        System.out.println(resp);
        Thread.sleep(5000);
    }
}

压测分析

由于每5s执行一次代码,所以调用其它服务接口间休眠5s,故new URL(request.url()).openConnection()模式下无法复用socket,即每次生成新的socket

发现平均时长为60ms+,比常规压测的时长40ms长了不少

调整为AbstractConnPool模式,同样的方式测试

通过上述结果可发现 此模式在一开始时耗时较高,之后性能在40ms,比上一模式性能提升50%

故 如果接口请求频繁,则两种模式均会利用socket,但是如果请求不频繁,如上述超过5s请求一次,则AbstractConnPool模式模式性能远远高于new URL(request.url()).openConnection()的性能。

但是使用AbstractConnPool模式时,需要注意设置每个route的最大连接数,以便及时回收连接

多线程测试

实现

  • feign 同上
  • controller 同上
  • 分别在使用feign-httpclient与不使用的情况下通过以下test类压测
@Test
    public void test() throws Exception {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    String resp = testFeign.test("34bde626faf04965b1023d334d9698fe");
                    System.out.println(resp);
                }
            }

        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    String resp = testFeign.test("34bde626faf04965b1023d334d9698fe");
                    System.out.println(resp);
                }
            }

        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }

压测分析

在使用AbstractConnPool模式时,服务器为两个tcp连接

使用HttpURLConnection时,同样会复用连接

总结

feign默认是使用的new URL(request.url()).openConnection(),该模式底层在短时间内也会复用socket连接,而feign-httpclient引入自管理的连接池,具体实现是使用的AbstractConnPool。

如果短时间的请求,则两种模式性能差别很小,如果间隔时间较长,则AbstractConnPool更优

参考