feignClient 微服务之间调用超时 status 500 ribbon_连接池

背景

    说一下我们的技术架构,我们目前是使用 SpringCloud Alibaba 版本,注册\配置中心nacos,接口间调用OpenFeign,网关用的gateway。

起因

    事情是这样的,我们这个项目之前是一个大的单体项目,最近一直在进行微服务拆分,拆出去的模块在单体项目中就用feign调用,这天业务人员在群里抱怨有一个页面很慢,之前也出现过这种情况,是公司网络的问题,因为在网关里看返回时间很短,100ms内,但是从服务器到公司之间很慢,打开网关的日志,准备甩锅给运维,但是这次情况好像不一样,定睛一看,网关里接口返回时间是12秒!!没看错是12秒!12000ms,这下炸锅了,赶紧开始找原因。 打开项目,看到源码,这是一个后台管理页面的一个请求列表数据,确实是有很多逻辑处理,每次返回10个数据,其中每次都要调用3个微服务,之前还是单体的时候,因为是本地方法调用,返回很快,整体接口返回在300ms内,这时候考虑feign调用网络耗时的问题,但是因为是内网调用,就算是http请求也应该很快才对啊。

feignClient 微服务之间调用超时 status 500 ribbon_微服务_02

查找原因

    这时候分别在3个调用feign接口的前后打印耗时时间,并且看调用的微服务的日志,我们每个微服务都会有一个fitter进行请求耗时统计,看到单体项目里每个feign接口调用耗时大概20-30ms,返回10个数据 每个都是90ms,加上其他的业务处理,怎么也得900ms以上,多调用几次后,发现有的微服务接口返回时间突然到了150ms+,这要是10个数据那不就慢的姥姥家去了,但是还好基本上慢feign调用出现的不是很多,这时候去微服务端查看,发现大部分时间很快1ms内就返回了,偶尔几个是100ms左右,左思右想,这微服务拆分出去后是多了网络消耗,但是不至于这么慢吧,本地远在执行1ms,加上网络消耗后最快也20ms,最慢100多ms,这里面肯定有问题。

  • 第一个锅是虚拟机的,主要是我们没使用对 ]

    先看看微服务端,因为执行的方法都一样,为什么有的1ms,有的却很耗时呢?看了看方法,并且没有锁,就是读mysql数据,后面再重复基本都是redis缓存了,那就只有1种可能垃圾回收!连上服务器,jstat -gc pid 1s 每秒打印一下,这一打印不得了,短短上线1周左右,FGC达到了48次,Ygc那就不用说了,首先我考虑是代码问题,会不会有内存泄漏?这个方法简单的令人发指,不应该有内存泄露啊? 看看堆的使用情况 jmap -heap pid ,好嘛,真相大白了,这堆大小只有300M+?要知道我们这台服务器可是4核8G的呀,原来是我们启动参数并没有加上初始化的堆大小,JVM默认初始化堆大小是物理内存的64/1,最大是4/1,也就是说我们的jvm已启动 堆大小是127,最大可以扩容到2G,每次满了就会触发FGC以及扩容,在测试环境重启了下项目,发现一启动就来了3次FGC,好嘛,赶紧加上启动参数,

-Xms4096m,-Xmx4096m,-XX:MetaspaceSize=256m,-XX:MaxMetaspaceSize=256m
固定了堆初始就4G,最大也4G不扩容,元空间规定256M

    再启动项目,调用看看,基本保持900ms左右,每个feign接口20-30ms,基本没有出现100+ms的feign接口了,GC情况也没有了。

  • 难道是http连接池链接不够用了?]

    但是就算20ms也不太对劲,明明我微服务里显示1ms就执行完毕返回了,网络消耗不能这么多吧?我们的feign是配置了httpClient客户端的,基于连接池,难道是连接数太少?看了一下,httpClient默认是200个连接,明明挺多啊,还是调大一些试试看,调到400,发版!不对劲,没有改善还是20-30ms。

  • 会不会有什么多余的配置? ]

    难道是我feign配置了编码解码的东西吗?正常来说不需要呀,看看代码配置,发现了如下代码:

feignClient 微服务之间调用超时 status 500 ribbon_微服务_03

果然是配置了编解码器,看着好像没啥用,删了试试,发版!

  • [完美!]

这下天空都晴朗了,单体项目在调用的feign的时候基本也是1-2ms,微服务耗时也是1-2ms,整体接口也缩小至100ms内返回了。

总结

  1. JVM堆大小需要定好,不然初始默认物理内存的64/1,容易触发GC,慢慢扩容也很慢,影响系统性能,feign在调用的时候如果对方系统在GC,相应的feign接口也会等待返回100ms虽然不多,但是如果请求多次那受不了。
  2. feign默认走的JDK的http客户端,每次请求都新建链接,需要改成httpClient、或者okHttp3基于连接池的http客户端。
  3. 如果没有什么其他的业务需要,不要配置feign的编解码器,非常的影响性能。

    到此一个接口从12s慢慢优化到100ms内,feign接口从偶尔100ms,平时20-30ms优化到1-2ms,和本地方法执行耗时没有区别。

    我是卢囧囧