前言

在前文记一次Nacos容器升级调优,我们完成所有服务器容器化部署并且稳定上线,但是压测阶段我们发现服务间调用的性能略差。对此我们不得不对服务器间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 {		

	}

}

关于HTTP协议一些概念

在切换客户端进行压测调整工具之前,我们必须对HTTP一些知识进行回顾补充:

Socket详解

socket即为网络通信套接字,一个socket是由源ip+源端口号和目的ip+目的端口号四元组构成。它是介于传输层和要用层之间的一个抽象层。所谓抽象层,我们可以理解为一组接口,socket用到了一种类似于外观模式的方式,将TCP、UDP等协议簇工作细节对上层屏蔽,使得我们要用到这些协议只需按需将参数传给Socket,让Socket为我们完成数据组织传输即可。
而我们常说的socket连接就是长连接,这就意味着除非客户端主动断开,否则这个连接就不会主动结束。

微服务降级原理 微服务调优_HTTP

HTTP长连接

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

如下图所示,请求头中带有Connection: keep-alive,就说明这个连接是长连接的。

微服务降级原理 微服务调优_微服务降级原理_02

了解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 可用)

以及笔者手动设置了feign的超时时间

feign:
  client:
    config:
      default:
        connectTimeout: 3000
        readTimeout: 3000

我们首先编写一个调用feign的代码,可以看到笔者在order服务中调用accout服务的http接口:

@GetMapping("testFeign")
    public ResultData<String> testFeign() {
        orderService.testFeign();
        return ResultData.success("success");
    }

testFeign的具体内容如下:

@Override
    public void testFeign() {
        productFeign.getByCode("P001");
        accountFeign.getByCode("zsy");
    }

再看看笔者的压测参数,创建1000个线程,分10次发送

微服务降级原理 微服务调优_客户端_03

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

微服务降级原理 微服务调优_微服务_04

基于httpclient

从上文源码中我们知道要想修改为httpclient客户端,包中必须得有ApacheHttpClient,所以我们在模块引入ApacheHttpClient相关的依赖

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

启动项目时,可以看到自动装配过程有引入ApacheHttpClient,说明配置生效了。

微服务降级原理 微服务调优_HTTP_05

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

微服务降级原理 微服务调优_微服务降级原理_06

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

微服务降级原理 微服务调优_jmeter_07

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

微服务降级原理 微服务调优_微服务_08

基于okhttp3

okhttp3配置步骤和前者差不多,首先引入依赖

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

然后在feign中增加okhttp.enabled为true的配置

feign:
  client:
    config:
      default:
        connectTimeout: 3000
        readTimeout: 3000
  okhttp:
    enabled: true

然后就可以进行压测了,最后压测结果如下,可以看出okhttp性能最优,所以笔者就将feign底层使用的客户端调整为okhttp3

微服务降级原理 微服务调优_jmeter_09

小结

本次调优过程其实不是很困难,只需注意切换客户端,根据业务要求设置实际参数(例如笔者的项目超时时间统一设置为3s),通过debug确认是否生效。
然后结合压测工具进行多次压测实验,避免偶然性,从中找出性能最好,准确率最高的样本进行比对。

参考文献

SpringCloud Alibaba微服务实战二十三 - Feign 性能调优

几个主流网络框架的比较

[JM_03]JMeter性能测试基础实战之QPS检测过程解析

前端 Spring Cloud Feign性能优化

HTTP持久连接

HTTP长连接