nmap扫描端口导致线上大量Java服务FullGC甚至OOM

最近公司遇到了一次诡异的线上FullGC保障,多个服务几乎所有的实例集中报FullGC,个别实例甚至出现了OOM,直接被docker杀掉。

观察报警服务的log,均有大量的此log

  1. *TNonblockingServer [ERROR] Read a frame size of ****, which is bigger than the maximum allowable buffer size for ALL connections.

注意到报警的几个服务都是net in流量突然有峰值,但是对应时间的http和rpc请求数没有增加。

此时已经开始怀疑是否有人恶意的调用接口。分析log应该与rpc接口有关,rpc端口外网访问不了,请求肯定来自内网。

后查明是公司内有人在用nmap扫描端口,nmap会向端口发送随机的数据包。公司内的JavaRpc采用的是Thrift,Thrift在解析异常数据包时有概率会申请大量内存,进而导致服务OOM。

分析了一波Thrift的源码,主要关注AbstractNonblockingServer类,以及其中的FrameBuffer。这两个类主要与Thrift解析数据流有关。

thrift采用NIO进行网络数据处理,NIO将数据放到buffer中,thrift再通过socketChannel读取buffer

thrift先从socketChannel中读取4个字节数据,4个字节转成int frameSize ,thrift就认作这个frameSize是整个数据包的size了,下一步就去申请对应大小的内存,再做进一步读取。

问题就出在了这里,如果是符合thrift IDL的数据包应该就没有问题,可是nmap发送的tcp包里的数据是随机的,而且我测试发现nmap一下会发几十个tcp请求过来。

thrift估计是为了避免这个问题,在拿到frameSize后,申请内存前,会有一个判断,当frameSize > MAX_READ_BUFFER_BYTES时,会把请求认作异常,不再进行处理。

  1. if (frameSize > MAX_READ_BUFFER_BYTES) {
  2. LOGGER.error("Read a frame size of " + frameSize
  3. + ", which is bigger than the maximum allowable buffer size for ALL connections.");
  4. return false;
  5. }

MAX_READ_BUFFER_BYTES默认是Long.MAX_VALUE,可以在thrift的TThreadedSelectorServer构造函数中进行指定,公司指定的值为100M。

这个100M仍然过大了,当frameSize小于100M,thrift仍然会申请内存的,而nmap会几秒钟发几十个请求过来,极端情况下这几十个请求均会申请<=100M的内存,最终导致服务的OOM

一个治标不治本的解决方法是调小MAX_READ_BUFFER_BYTES。然而将MAX_READ_BUFFER_BYTES调整的过小的话也要考虑是否影响到正常的RPC请求。

总之,考虑到thrift如此迷惑的设计,运维应该在网络上做更多的限制可能会更好一点,首先RPC端口外网禁止访问,避免心怀不轨的人来搞破坏,其次内网也应该建立规范,比如扫描时尽量有意的避开绕过RPC端口。

核心代码

  1. public boolean read() {
  2. if (state_ == FrameBufferState.READING_FRAME_SIZE) {
  3. // try to read the frame size completely
  4. if (!internalRead()) {
  5. return false;
  6. }
  7. // if the frame size has been read completely, then prepare to read the
  8. // actual frame.
  9. if (buffer_.remaining() == 0) {
  10. // pull out the frame size as an integer.
  11. int frameSize = buffer_.getInt(0);
  12. if (frameSize <= 0) {
  13. LOGGER.error("Read an invalid frame size of " + frameSize
  14. + ". Are you using TFramedTransport on the client side?");
  15. return false;
  16. }
  17. // if this frame will always be too large for this server, log the
  18. // error and close the connection.
  19. if (frameSize > MAX_READ_BUFFER_BYTES) {
  20. LOGGER.error("Read a frame size of " + frameSize
  21. + ", which is bigger than the maximum allowable buffer size for ALL connections.");
  22. return false;
  23. }
  24. // if this frame will push us over the memory limit, then return.
  25. // with luck, more memory will free up the next time around.
  26. if (readBufferBytesAllocated.get() + frameSize > MAX_READ_BUFFER_BYTES) {
  27. return true;
  28. }
  29. // increment the amount of memory allocated to read buffers
  30. readBufferBytesAllocated.addAndGet(frameSize + 4);
  31. // reallocate the readbuffer as a frame-sized buffer
  32. buffer_ = ByteBuffer.allocate(frameSize + 4);
  33. buffer_.putInt(frameSize);
  34. state_ = FrameBufferState.READING_FRAME;
  35. } else {
  36. // this skips the check of READING_FRAME state below, since we can't
  37. // possibly go on to that state if there's data left to be read at
  38. // this one.
  39. return true;
  40. }
  41. }
  42. // it is possible to fall through from the READING_FRAME_SIZE section
  43. // to READING_FRAME if there's already some frame data available once
  44. // READING_FRAME_SIZE is complete.
  45. if (state_ == FrameBufferState.READING_FRAME) {
  46. if (!internalRead()) {
  47. return false;
  48. }
  49. // since we're already in the select loop here for sure, we can just
  50. // modify our selection key directly.
  51. if (buffer_.remaining() == 0) {
  52. // get rid of the read select interests
  53. selectionKey_.interestOps(0);
  54. state_ = FrameBufferState.READ_FRAME_COMPLETE;
  55. }
  56. return true;
  57. }
  58. // if we fall through to this point, then the state must be invalid.
  59. LOGGER.error("Read was called but state is invalid (" + state_ + ")");
  60. return false;
  61. }

参考:https://my.oschina.net/shipley/blog/422204

nmap扫描端口导致线上大量Java服务FullGC甚至OOM的更多相关文章

  1. 一次性搞清楚线上CPU100%,频繁FullGC排查套路

    “ 处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及 Full GC 次数过多的问题. 当然,这些问题最终导致的直观现象就是系统运行缓慢,并且有大量的报警. 本文主要针对系统 ...

  2. Java服务,内存OOM问题如何快速定位? (转)

    转自:公众号  架构师之路 问题:有一个Java服务出现了OOM(Out Of Memory)问题,定位了好久不得其法,请问有什么好的思路么? OOM的问题,印象中之前写过,这里再总结一些相对通用的方 ...

  3. 关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程

    某线上应用在进行查询结果导出Excel时,大概率出现持续的FullGC.解决这个问题时,记录了一下整个的流程,也可以作为一般性的FullGC问题排查指导. 1. 生成dump文件 为了定位FullGC ...

  4. 关于nmap扫描端口

    nmap查看一个服务器的端口,是通过扫描来实现的.所以在本机执行nmap扫描的端口有可能被防火墙阻止,在外部是访问不了的. 如:开启ORACLE监听后,在本机使用nmap 127.0.0.1是可以扫描 ...

  5. 案例分享 | dubbo 2.7.12 bug导致线上故障

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star.搜索关注微信公众号"捉虫大师",后端技术分享,架构设计.性能优化.源码阅读. ...

  6. 【Maven篇】---解决Maven线上部署java.lang.ClassNotFoundException和no main manifest attribute解决方法

    一.前述 maven 线上部署的话会出现一些问题比如java.lang.ClassNotFoundException或者no main manifest attribute的话,是因为maven 配置 ...

  7. 记一次log4j日志导致线上OOM问题案例

    最近一个服务突然出现 OutOfMemoryError,两台服务因为这个原因挂掉了,一直在full gc.还因为这个问题我们小组吃了一个线上故障.很是纳闷,一直运行的好好的,怎么突然就不行了呢... ...

  8. CentOS上部署JAVA服务【转】

    http://www.th7.cn/Program/java/201511/686437.shtml 本文将介绍如何在CentOS上运行Java Web服务,其中将包括如何搭建JAVA运行环境.如何开 ...

  9. nmap 扫描端口 + iftop 实时监控流量

    sleep 1|telnet 127.0.0.1 223 nmap 127.0.0.1 -p 223 -PN   (对禁ping IP) iftop -P -n -B -B 按字节显示 -N 切换 端 ...

随机推荐

  1. k8s configmap 挂载配置文件

    转自https://blog.csdn.net/weixin_34102807/article/details/85965725 1.新建ConfigMap apiVersion: v1 kind: ...

  2. python set 一些用法

    add(增加元素) name = set(['Tom','Lucy','Ben']) name.add('Juny') print(name)#输出:{'Lucy', 'Juny', 'Ben', ' ...

  3. 问题:dependencyManagement和dependencies有什么区别

    dependencyManagement和dependencies有什么区别 一.Maven的包管理 在maven中,dependencyManagement.dependencies和depende ...

  4. 【网络协议】OSI七层模型 和TCP/IP五层模型

    OSI(Open System Interconnection)七层模型 TCP/IP 五层模型

  5. 一个DDOS木马后门病毒的分析

    http://blog.csdn.net/qq1084283172/article/details/49305827 一.样本信息 文件名称:803c617e665ff7e0318386e24df63 ...

  6. Linux文件共享服务之NFS

    NFS(Network File System) 网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源.在NFS的应用中,本地NFS的客户端应用可 ...

  7. 缓冲区溢出之栈溢出利用(手动编写无 payload 的 Exploit)

    0x01 介绍 Exploit 的英文意思就是利用,它在黑客眼里就是漏洞利用.有漏洞不一定就有Exploit(利用),有Exploit就肯定有漏洞.编写缓冲区溢出的Exploit分为3个方面:漏洞溢出 ...

  8. Mybatis-Plus02 CRUD

    先将快速开始01看完,再看这个文档 配置日志 我们所有的sql现在都是不可见的,我们希望知道它是怎么执行的,所以我们就必须看日志,开发的时候打开,上线的时候关闭 在application.proper ...

  9. PHP 上传文件至阿里云OSS对象存储

    简述 1.阿里云开通对象存储服务 OSS 并创建Bucket 2.下载PHP SDK至框架扩展目录,点我下载 3.码上code 阿里云操作 开通对象存储服务 OSS 创建 Bucket 配置Acces ...

  10. Java_封装

    分类(分层)思想 dao层(数据访问层):对数据进行管理的操作(增.删.改.查). 数据库.数组.集合 service层(业务层): 具体做一些业务操作 controller(控制层): 用来接收用户 ...