tomcat项目接口每隔一段时间出现延迟慢

一、问题出现

之前在搞性能优化,发现有些通过Spring boot feign的RPC接口比较慢,达到100-200ms,按理RPC调用都是IP直连,不应该超过100ms的,然后使用阿里开源的arthas工具测试接口执行时间,结果发现同一个接口大概每10秒会出现一次慢100+ms,如图:

1538d484f5ac35ec701c6f20c95792348e8.jpg

但实际上服务方的API层响应很快:

b1962feffff11ec8fc919b55a33ff4eaf5b.jpg

二、原因分析

1、负载均衡到其它开发机

疑点:由于个人开发机连接测试环境的注册地址,有可能测试接口会调用个人地址,但测试机ip为10.8,个人开发机为192.168,但若是分到个人机器应该不止100ms。

操作:使用单独的测试机依然重现

结论:排除此原因

2、httpclient配置出错

疑点: 1)怀疑线程keepalive太短,过期后重新开启线程,增加延迟,查看代码确实设置了keepalive=5,但开启线程不应该100ms那么多

2)怀疑配置出错导致负载均衡算法延迟

操作:使用arthas测试执行时间,如下图,可以看出最慢是读取流操作了

image2019-6-25_18-5-20.png?version=1&modificationDate=1561457121058&api=v2

结论:排除此原因

3、服务方的tomcat配置问题

疑点: 既然不是调用者问题,那就从接收者身上找问题好了,从tomcat接收请求开始跟踪,果然发现执行时间长的位置

image2019-6-25_18-17-15.png?version=1&modificationDate=1561457835213&api=v2

这是什么模块呢?简单来说就是加载jar中的静态资源,接口请求过来之后需要判断是否命中jar中的静态资源。查看源码发现每次加载完会缓存起来,但隔一段时间就重新加载,表明应该有地方清了缓存。搜索发现有个后台线程ContainerBackgroundProcessor执行gc方法,方法内会将缓存设null

// ContainerBackgroundProcessor部分代码
while(!ContainerBase.this.threadDone) {
    try {
        Thread.sleep((long)ContainerBase.this.backgroundProcessorDelay * 1000L);
    } catch (InterruptedException var8) {
        ;
    }
    if (!ContainerBase.this.threadDone) {
        // 用于重载standardContext、处理过期的session、清除jar中的静态资源缓存等等
        this.processChildren(ContainerBase.this);
    }
}
// AbstractArchiveResourceSet清除jar中的静态资源缓存
public void gc() {
    Object var1 = this.archiveLock;
    synchronized(this.archiveLock) {
        if (this.archive != null && this.archiveUseCount == 0L) {
            try {
                this.archive.close();
            } catch (IOException var4) {
                ;
            }
            this.archive = null;
            this.archiveEntries = null;
        }
 
    }
}

由于backgroundProcessorDelay默认为10,所以每10秒清一次缓存,这也印证了现象中的情况!注意ContainerBackgroundProcessor只清除是jar中的静态资源,项目中的静态资源并不会缓存于此,也就不会有影响了。

通过debug发现,每次加载的静态资源出处是springfox-swagger-ui.jar(自动生成api文档),里面的确存在静态资源,于是将其依赖去掉,发现延迟100ms的情况也消失了!

29577d988a4f26d13904f1e28677e20c1be.jpg

结论:tomcat在启动时,通过StaticResourceConfigurer.addResourceJars将所有jar中的静态资源路径保存到list中,请求过来时先从此list中加载全部资源并检查是否命中,是则返回,若所有jar均没有静态资源则不会加载;为了增加性能,在加载完静态资源后会缓存下来,但设置了一个后台线程,定时清除缓存(个人认为:tomcat这样做应该是为了热部署,但完全没有配置控制不gc的确有点沙雕,虽然耗时只有100+ms)。

三、结论

最终结论:没事不要把静态资源扔到jar里!!!

最终结论:没事不要把静态资源扔到jar里!!!

最终结论:没事不要把静态资源扔到jar里!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值