dubbo调用超时问题导致的JVM内存溢出

dubbo调用超时似乎和JVM内存溢出没有太直接的关联,但上周在项目中出现的一个dubbo调用超时异常,在尝试解决的时候引起了JVM内存溢出问题。

问题起因:本人在公司是负责即时通讯服务的,某天正准备下班回家,突然接到运营部门的电话说现在有一个紧急的通知要做全员发布,但是发布之后所有人都没有收到。接到这个电话之后,我脑子中的第一反应是最近也没对服务器做过任何的更改,怎么会出现发布失败的问题呢。发布全员消息使用的是pubaccount的模块服务,半小时前还收到了使用同一个模块发出来的通知消息。因此我觉得是运营人员自己操作的问题。但是虽然这样,还是本着负责的态度先从服务端日志查起。

解决过程:

    登录上服务器,打开logs,直奔erro去查,发现确实有问题,服务器打印出了大量的dubbo timeout异常,该异常是在调用一个dubbo服务的时候超时引起的,大部分情况下,该异常的出现时服务提供者异常引起的。因此我迅速登到另一台服务器查看服务提供者的进程是否存在,日志是否正常,发现提供者服务没有任何的问题。这就说明该超时异常并不是没有找到服务提供者而是找到之后在调用的时候服务提供者没有在规定的时间内返回。

    因此我翻找代码,定位到原来发布全员通知的时候需要调用用户服务获取所有用户的用户名集合,我查询数据库发现,用户数大概在20万左右,因此我感觉是由于获取这20万用户名集合太过耗时导致的,用户名集合是在缓存中存储的,理论上缓存的速度是足够满足的,但我还是抱着试一试的态度将对应的dubbo服务的超时时间从3s改成了30s。修改-保存-重启服务,立马进行测试,发现超时异常确实不存在了,但却有报了另一个异常:

com.alibaba.dubbo.remoting.transport.AbstractCodec.checkPayload() ERROR Data length too large: 11557050, max payload: 8388608 java.io.IOException: Data length too large: 11557050, max payload: 838860

该异常很好理解,表示方法返回的数据大小超出了dubbo默认的大小,无奈只能再次更改dubbo的默认payload参数,再次修改-保存-重启服务-测试,哈哈,上述的异常都没有了,服务正常启动了,于是本人高兴的下班回家。

故事并没有结束,回家的路上,运营同事又打来电话说现在不仅全员消息发不了,连单独发给几个人都发不了了,我思前想后只改了dubbo的最大数据长度怎么还会影响整个服务?话不多说立马回头直奔公司。

到了公司打开电脑,登录服务器发现日志显示java heap OutOfMemory,如果是平常,看到这个异常我或许会按部就班的dump堆内存,然后分析溢出原因,但这次我看到这个的短暂瞬间就想到了问题大概在哪,我查看该服务的jum参数配置,发现堆内存只设置了60兆的大小,而调用dubbo服务返回的用户名集合有70兆,根据JVM的垃圾回收原理,JVM会将大对象(比如大的集合)直接晋升到老年代,因此导致了内存溢出的异常。

最后我调整了服务启动的jvm参数配置,重启-测试,所有消息正常收发,这才安心的下班回家。

这次经历也告诫我任何一个看似无关的改动都有可能引起一连串的连锁反应,因此永远不要说:我没改代码,不是我的问题。


你可能感兴趣的:(dubbo,jvm)