一次RPC调用时间都去哪儿了
这个思考从一次撕逼开始,我们给用户组提供了一个JSF(京东自研RPC框架)接口,现象:用户组UMP(京东自研性能监控)监控接口调用超过500ms,我们这边监控max值不超过200ms。翻译一下,具体点就是下面这个描述。
服务端代码执行前后添加监控,最大耗时200ms
调用端调用前后添加监控,最大值500ms
针对此现象,不撕逼,不科学。
但一次RPC调用时间都去哪儿了呢?下面是计算方法。
一次正常的RPC调用时间
分析如下:
一次正常的调用统计的耗时主要包括:①调用端RPC框架执行时间 + ②网络发送时间 + ③服务端RPC框架执行时间 + ④服务端业务代码时间。
① 调用端调用的时候RPC框架会首先拦截业务请求,同时将对象序列化,还有就是接收到响应的时候会反序列化;这个时候框架耗时主要与cpu、jvm运行情况相关,序列化耗时主要和传输对象复杂程度相关。
② 网络时间就是数据包在网络传输过程中的时间,包括 请求+响应,耗时主要与数据包大小、网络情况相关。
③ 服务端主要是队列等待时间,请求拦截+反序列、响应的序列化;
如果RPC框架使用了队列,如果配置了可能有一定的等待时间,成熟的RPC框架都会支持队列的方式,不然就是默认线程当队列使用了。
这个时候框架耗时主要与cpu、jvm运行情况相关,序列化耗时主要和传输对象复杂程度相关。
注意如果服务端较忙(线程比较多,然后cpu超过80%),可能从 服务端到接到请求 到 丢到业务线程池给服务端业务代码执行,线程切换也有一定耗时。
④ 服务端业务代码时间才是ump收集的服务端耗时。
另外,如果是一些异常请求(例如服务端线程池已满,客户端超时重试等),其实根本没有打到服务端业务代码,服务端未记录耗时,但是调用端已经记录了这些耗时。所以当时的情况还要看下有无异常情况。
解决方法:
1.首先第一想到的就是要分析网络情况,看是否网络延迟严重,是否有TCP重传
2.分析服务端和调用端的运行情况,看是否压力较大,比如cpu使用率,cpu负载,内存占用大小等。
3.看传输对象是否很大,很复杂,请尽量简单,这个对序列化有很大影响。
4.如果服务端有队列,试着减少队列,或者改为固定线程池,线程特别多,可以试试减少线程大小。
5.控制cpu不要太高,尽量不超80%,不行该扩容就扩容。另外我觉得大部分业务都是I/0密集型,并非计算密集型,这里有个公式,核数*7,比如4C的容器,那么2.8%的使用率应该就很高了。达到80%的时候一定有问题了,要么线程阻塞,要么就赶紧扩容吧。
6.有可能的话,可以以单次耗时为准,查看下单次耗时的差距,从而看看是否是某些参数的请求导致耗时比较长。
7.看看服务端是否有full gc,你懂的。STW。如果频繁yong gc也不是好事儿,无论yong还是full都会STW,只是耗时长短问题。
8.排除了以上种种,还没有定位到原因,那么就需要尝试如下方法了:可在服务端通过tcpdump抓包,用wireshark分析rpc请求在服务端耗时,定位是服务端还是调用端耗时长,然后进一步确定原因。不过这一步,往往需要运维人员配合操作。