记一次session导致线上oom的问题排查

248 阅读2分钟

问题出现

image.png

公司的平台base服务最近会在运行一段时间后oom,当发现这问题后,我们在测试环境中尝试复现这个问题。

排查过程

首先idea中配置jvm参数 -XX:+HeapDumpOnOutOfMemoryError ,这个指令会在oom的时候打印一份dump文件。

在漫长的等待后终于复现了这个问题。。。。

我们使用jvisualvm和MAT这两个工具对生成的dump文件进行分析:

image.png

可以发现problem suspect提示StandardManager类占了堆内存的将近80%,继续深入排查发现是sessions出现了问题,存了大量的session数据,这些数据无法被回收,从而导致oom。

image.png

通过查看StandardManager和他的父类的相关源码我们可以了解到StandardManager里的sessions集合用来存放用户session数据,平常获取session是用的是HttpServletRequest#getSession()方法获取的,其真正调用的方法为org.apache.catalina.connector.Request#doGetSession

这里就会发现如果doGetSession获取的session为null的话,最终会调用org.apache.catalina.Manager#createSession方法

image.png

而这个方法会创建一个session,并将其放入到sessions中维护

image.png

那么我们可以推断出是有接口不断地调用了 httpServletRequest.getSession()而上边的doGetSession方法为null,使得不断的创建新session导致了oom。

找到了问题的根源后我们去排查了项目中使用session的地方:

这是访问服务http接口需要走的鉴权拦截器的部分代码。

大概做了几件事:

  • 先用token鉴权
  • 通过后获取session里的用户信息和权限

我们反复排查发现传入token确实为用户正确的token,但在使用getSession的时候获取的sessionId却不是登录时存的sessionId。

经查阅发现session在IP相同的情况下为同一个作用域,而线上环境是所有服务部署在同一台服务器上的,所以平台的基本功能是可以使用的,也不会有多创建session的情况。

而对于srs视频服务来说,因为使用了nginx做转发,属于不同作用域,所以每开启一次视频,每一次视频保活都会创建新的session,从而导致线上oom,这也是问题很难定位的原因。

而对于线下环境来说,前后端分离的情况下前端ip和后端ip不同,所以基本功能的使用也会不断的创建session。

解决方法:

在分布式服务的环境下使用分布式session的解决方案来替代,比如用redis替代session存放数据

问题总结:

  1. httpServletRequest获取session的时候,如果前端有传,那么会使用传入的。如果没有传会创建一个新的。
  2. 在分布式情况下要注意作用域的问题,使用分布式session来替代。