基于压测进行Feign调优
# 前言
在前文记一次Nacos容器升级调优 (opens new window),我们完成所有服务器容器化部署并且稳定上线,但是压测阶段我们发现服务间调用的性能略差。对此我们不得不对服务器间WebService客户端openFeign进行调优。
可能读者问到,为什么你们的服务通信要用openFeign而不是RPC呢?针对笔者开发的b端系统来说,大多数请求都是基于http暴露给前端使用的。而且因为业务原因,很多交互需要基于http进行通信,这就使得很多服务间通信的接口只能基于那些http接口进行通信。
而且这种做法也能避免编写重复的代码,所以我们就索性使用openFeign来实现服务间的通信。
# 如何提升Feign的执行效率
笔者查阅网上资料了解到,我们实际上可以通过自动装配修改feign底层使用的客户端框架。这一点我们可以在源码中看到答案。
代码如下,可以看到FeignAutoConfiguration 这个自动装配类,会扫描当前包中是否存在ApacheHttpClient或者OkHttpClient(或者配置中配置了feign.okhttp.enabled为true)。即可实现底层客户端的修改。
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
public class FeignAutoConfiguration {
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
//该属性默认为true,所以只要导入ApacheHttpClient类即可完成自动装配
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
.......
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 关于HTTP协议一些概念
在切换客户端进行压测调整工具之前,我们必须对HTTP一些知识进行回顾补充:
# Socket详解
socket即为网络通信套接字,一个socket是由源ip+源端口号和目的ip+目的端口号四元组构成。它是介于传输层和要用层之间的一个抽象层。所谓抽象层,我们可以理解为一组接口,socket用到了一种类似于外观模式的方式,将TCP、UDP等协议簇工作细节对上层屏蔽,使得我们要用到这些协议只需按需将参数传给Socket,让Socket为我们完成数据组织传输即可。
而我们常说的socket连接就是长连接,这就意味着除非客户端主动断开,否则这个连接就不会主动结束。

# HTTP长连接
所谓HTTP长连接其实说的是TCP长连接,若HTTP使用的是TCP长连接,这就意味一次HTTP会话结束不会将当前HTTP所使用的TCP连接关闭,虽然有一点的性能开销,但下次需要建立连接时就无需再次进行TCP三次握手的建立连接过程,通信效率就相对高很多。
如下图所示,请求头中带有Connection: keep-alive,就说明这个连接是长连接的。

# 了解openFeign底层可以使用的三大客户端
# HttpURLConnection
这个是openFeign默认使用的客户端,这个客户端仅仅是对HTTP请求做了封装并没有做任何优化处理,它的通信过程很简单,通过outputStream将数据写到流中,即使写完也不会立即发送,必须等客户端将流关闭之后才会从缓存区的内容生成HTTP正文。
HttpURLConnection还有一个特点就是它并没有什么超时机制,这可能会导致在网络故障的情况下出现请求僵死的情况。
# HttpClient
HttpClient相比于前者做到的池化,通过池化技术实现连接持久化,使用上也比前者更加方便,但它并没有对相同的请求进行缓存。
# okHttp
okHttp同样也做到池化,而且相较于HttpClient它还做到的响应缓存,即相同的请求结果它会缓存起来。
okHttp不仅仅实现了池化,还做到了公用socket,即相同的ip和端口会重用同一个socket,这就意味着每次建立连接不仅无需进行TCP三次握手,连建立请求socket的步骤都省略了。
# 实践——基于压测了解三大客户端性能优劣
# 基于HttpURLConnection
接下来我们就基于jmeter来压测不同客户端的性能,首先介绍一下笔者的机器:
1. 处理器:11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz 2.30 GHz
2. 16.0 GB (15.6 GB 可用)
2
以及笔者手动设置了feign的超时时间
feign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 3000
2
3
4
5
6
我们首先编写一个调用feign的代码,可以看到笔者在order服务中调用accout服务的http接口:
@GetMapping("testFeign")
public ResultData<String> testFeign() {
orderService.testFeign();
return ResultData.success("success");
}
2
3
4
5
testFeign的具体内容如下:
@Override
public void testFeign() {
productFeign.getByCode("P001");
accountFeign.getByCode("zsy");
}
2
3
4
5
再看看笔者的压测参数,创建1000个线程,分10次发送

笔者基于这个参数,进行多组测试,找了一条平均的实验结果:

# 基于httpclient
从上文源码中我们知道要想修改为httpclient客户端,包中必须得有ApacheHttpClient,所以我们在模块引入ApacheHttpClient相关的依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2
3
4
启动项目时,可以看到自动装配过程有引入ApacheHttpClient,说明配置生效了。

若读者对此还有疑问,可以可以在feign调用的代码段打个debug,不断深入调试,可以看到最终的执行会走到ApacheHttpClient

确定成功装配之后,我们就可以进行压测了,同样的压测过程,数据如下,可以看到尽管我们对客户端进行了池化,但是性能相对于默认的HttpURLConnection还要差。

查阅底层池化配置确实没有问题,查阅网上说法是由于其api众多,是我们很难再不破坏兼容性的情况下对其进行扩展。所以,Android团队对提升和优化httpclient积极性并不高,android5.0被废弃,6.0逐渐删除。所以在性能方便可能也没有做出很大的优化。

# 基于okhttp3
okhttp3配置步骤和前者差不多,首先引入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
2
3
4
然后在feign中增加okhttp.enabled为true的配置
feign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 3000
okhttp:
enabled: true
2
3
4
5
6
7
8
然后就可以进行压测了,最后压测结果如下,可以看出okhttp性能最优,所以笔者就将feign底层使用的客户端调整为okhttp3

# 小结
本次调优过程其实不是很困难,只需注意切换客户端,根据业务要求设置实际参数(例如笔者的项目超时时间统一设置为3s),通过debug确认是否生效。
然后结合压测工具进行多次压测实验,避免偶然性,从中找出性能最好,准确率最高的样本进行比对。
# 参考文献
SpringCloud Alibaba微服务实战二十三 - Feign 性能调优 (opens new window)
几个主流网络框架的比较 (opens new window)
OkHttp3系列(一)初识OkHttp3 (opens new window)
[JM_03]JMeter性能测试基础实战之QPS检测过程解析 (opens new window)
如何精准统计出你的接口“QPS”是多少 (opens new window)
前端 Spring Cloud Feign性能优化 (opens new window)
HttpClient、OKhttp、RestTemplate接口调用对比,选择一个优秀的 HTTP Client 的重要性 (opens new window)