简述: Ambari的Nagios会不断发信息到hiveserver2的10000端口,以做健康监测,但hiveserver2的thrift会将其信息错误解读而导致OOM
问题发现: 在hiveserver2所在的节点,查看/var/log/hive/hive-server2.log 发现hiveserver2会莫名其妙不断OutOfMemory: Java heap space
问题排查:
加入日志和OOM heap dump
打开gc日志: 在/usr/lib/hive/bin/ext/hiveserver2.sh第18行加入: export HADOOP_CLIENT_OPTS="${HADOOP_CLIENT_OPTS} -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/hive/gc.hiveserver2.log-`date +'%Y%m%d%H%M%S'` -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/hive -XX:+DisableExplicitGC -XX:+UseCompressedOops"
在OOM时会进行heap dump
用jvisualvm分析heap dump文件, 并结合hive-server2.log中的OOM错误堆栈进行排查:
日志中的OOM堆栈:
Exception in thread "pool-5-thread-5" java.lang.OutOfMemoryError: Java heap space
at org.apache.thrift.transport.TSaslTransport.receiveSaslMessage(TSaslTransport.java:181)
at org.apache.thrift.transport.TSaslServerTransport.handleSaslStartMessage(TSaslServerTransport.java:125)
at org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java:253)
at org.apache.thrift.transport.TSaslServerTransport.open(TSaslServerTransport.java:41)
at org.apache.thrift.transport.TSaslServerTransport$Factory.getTransport(TSaslServerTransport.java:216)
at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java:189)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
heap dump中的OOM堆栈(通过jvisualvm查看):
"pool-5-thread-1" prio=5 tid=18 RUNNABLE
at java.lang.OutOfMemoryError.<init>(OutOfMemoryError.java:48)
at org.apache.thrift.transport.TSaslTransport.receiveSaslMessage(TSaslTransport.java:181)
at org.apache.thrift.transport.TSaslServerTransport.handleSaslStartMessage(TSaslServerTransport.java:125)
at org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java:253)
at org.apache.thrift.transport.TSaslServerTransport.open(TSaslServerTransport.java:41)
Local Variable: org.apache.thrift.transport.TSaslServerTransport#1
at org.apache.thrift.transport.TSaslServerTransport$Factory.getTransport(TSaslServerTransport.java:216)
Local Variable: java.lang.ref.WeakReference#200
Local Variable: org.apache.thrift.transport.TSocket#2
at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java:189)
Local Variable: org.apache.hive.service.auth.TSetIpAddressProcessor#1
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
Local Variable: java.util.concurrent.ThreadPoolExecutor#4
Local Variable: org.apache.thrift.server.TThreadPoolServer$WorkerProcess#1
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
Local Variable: java.util.concurrent.ThreadPoolExecutor$Worker#1
at java.lang.Thread.run(Thread.java:744)
由于栈底是一个Thread,结合抛出异常的类名和方法名,猜测是一个与通信相关的监听线程所抛出的OOM.
下载相应版本的hive源码(0.13.0)与thrift源码, 在hive源码中查找上述jvisualvm查看的OOM异常栈中与thrift相关的类是在hive的那个地方调用了
从底查找OOM栈中并非是java原生类的thrift类,因为从OOM栈可以发现这基本是thrift的类之间的调用,也就是可以猜测是hive建了一个Thread去调用在该栈从底往上锁遇到的第一个thrift类的实例(此处实例用Local Variable标注,说明相应的类是被实例化来使用了的). 此处从底往上所遇到的第一个thrift类是org.apache.thrift.server.TThreadPoolServer.
发现在hive的源代码中的HiveMetaStore, HiveServer, ThriftBinaryCLIService中都用了TThreadPoolServer, 而由于HiveMetaStore和HiveServer都是在其main方法内实例化了TThreadPoolServer, 因此可排除这两个类. 而ThriftBinaryCLIService是一个Runnable, 在其run()方法内实例化了TThreadPoolServer, 因此若TThreadPoolServer内部的调用中发生了OOM则很有可能出现与该次OOM栈相似的异常栈形式, 所以我们着手深入了解一下ThriftBinaryCLIService.run()方法内对TThreadPoolServer进行调用,以及TThreadPoolServer相关的内部调用代码.
由于OOM从"TSaslTransport.receiveSaslMessage(TSaslTransport.java:181)"抛出,从离OOM抛出点最近的类实例信息"Local Variable: org.apache.thrift.transport.TSaslServerTransport#1"并结合代码可知,TSaslServerTransport是TSaslTransport的子类,在调用TSaslServerTransport.open()中间接调用了其父类的TSaslTransport.receiveSaslMessage()方法,而OOM就在TSaslTransport.receiveSaslMessage()方法中被触发.
通过查看源码发现"TSaslTransport.receiveSaslMessage(TSaslTransport.java:181)"处的代码是"byte[] payload = new byte[EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES)];", 初步猜测是EncodingUtils.decodeBigEndian()方法解析出来的数字太大,导致创建了一个超大的byte[]而导致OOM.
在jvisualvm的heap dump分析器中对类实例按占用内存大小排序,可看到byte[]的确占了很多内存.
在jvisualvm的heap dump分析器中查看org.apache.thrift.transport.TSaslServerTransport对应的实例在OOM时的状态.
由于代码"byte[] payload = new byte[EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES)];"是通过对messageHeader进行解析而得到一个整数,因此可以看看OOM时messageHeader中的内容是什么.
拿到messageHeader当时的内容后,可以写一个main方法简单看看"new byte[EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES)]"所得到的数组有多大,最后发现数组有近1G多那么大. 而按照当时hiveserver2的内存配置,堆也就约1G的内存,突然申请那么大的byte[],不OOM才怪呢.
那么是什么导致"EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES)"得到的整数那么大呢,想必应该是发过来的信息不符合thrift协议规范,而又如何找到对方发了什么信息过来呢? 以及对方是谁? 为什么要发这些信息呢?
我们从OOM堆栈中得知问题是发生在调用链 TThreadPoolServer->TSaslServerTransport->TSaslServerTransport的super父类TSaslTransport上的, 通过源代码得知,TSaslTransport.receiveSaslMessage()是用于读取远端连接过来的client端的信息的,具体的读取通过TSaslTransport.underlyingTransport去读取. 而TSaslTransport.underlyingTransport则代表了一个客户端的连接,在TThreadPoolServer.serve()中由代表服务端的TThreadPoolServer.serverTransport_通过监听获得.因此,我们可以从TSaslTransport.underlyingTransport入手,获得连接到服务端的远端IP和端口.
首先,找到TSaslTransport.underlyingTransport, 并查看它内部的信息.
查看TSaslTransport.underlyingTransport实例的socket_.impl.localport,其值为10000即本地ThriftBinaryCLIService所对外暴露的端口,而TSaslTransport.underlyingTransport实例的socket_.impl.port为远端访问这个10000的远端端口,值为58235. TSaslTransport.underlyingTransport实例的socket_.impl.address.holder.address为"-1062694615",是访问本地10000端口的远端客户端的IP的整数表达形式,可参考Inet4Address.getAddress()把这个整数转化成我们所熟悉的IP地址字符串.
把远端客户端ip的整数表达转换成ip字符串的代码如下:
public static void convertInt2IPString() { int address = -1062694615; //copy from the instance of InetAddress.holder.address byte[] ipByteArr = getAddress(address); String ipStr = numericToTextFormat(ipByteArr); System.out.println(ipStr);//整数ip为-1062694615,转换成常规ip为192.168.145.41 } //copy from Inet4Address.getAddress() public static byte[] getAddress(int address) { final int INADDRSZ = 4; byte[] addr = new byte[INADDRSZ]; addr[0] = (byte) ((address >>> 24) & 0xFF); addr[1] = (byte) ((address >>> 16) & 0xFF); addr[2] = (byte) ((address >>> 8) & 0xFF); addr[3] = (byte) (address & 0xFF); return addr; } //copy from Inet4Address.numericToTextFormat() static String numericToTextFormat(byte[] src) { return (src[0] & 0xff) + "." + (src[1] & 0xff) + "." + (src[2] & 0xff) + "." + (src[3] & 0xff); }
得到"-1062694615"转换成的ip为:"192.168.145.41".
用命令"ss -anpe | grep 10000"在本地运行,结果如下:
LISTEN 0 50 *:10000 *:* users:(("java",20804,373)) uid:1006 ino:1760863 sk:ffff88003ed12080
CLOSE-WAIT 1 0 192.168.145.42:10000 192.168.145.41:36315 users:(("java",20804,364)) uid:1006 ino:2266968 sk:ffff88003ec93480
其中20804的确是hiveserver2的进程, 访问本地10000端口的远端客户端ip的确也是192.168.145.41,但客户端的远端端口却不是58235.
在客户端192.168.145.41运行"ss -anpe | grep 10000",发现每运行一次该命令,访问192.168.145.42:10000的端口都会变化,且状态是TIME_WAIT,这说明与192.168.145.42:10000的连接已经被192.168.145.42关闭,因此猜测由于192.168.145.42的OOM导致192.168.145.41的某个程序无法连接到10000端口,而连接10000的那个程序就不得不变化端口,以求连接到192.168.145.42:10000上.
由于客户端发送信息的端口一直在变,而且用ss命令查看相关端口都只能看到等待状态而无法看到使用该端口的线程,我们就无法获得通过这些端口发送消息的进程的信息. 因此我们打算看看发送到192.168.145.42服务端10000端口的消息到底是怎样的.
使用"tcpdump -i eth4 -s 0 -nnA 'port 10000' -w /mnt/shareDisk/local10000.cap" 抓取访问10000端口的数据包,并保存到local10000.cap中. 在windows中用Wireshark打开cap文件,查看通信报文内容.发现所有来自192.168.145.41的报文体都包含字符串"A001". 但这条线索依然无法帮助我们定位到底是什么程序发了字符串"A001"到192.168.145.42的10000端口.
由于猜测要访问192.168.145.42的hiveserver2的10000端口的应该就是大数据集群内部的东西,因此我们通过Ambari的页面逐一关闭大数据集群的service,再通过192.168.145.42的tcpdump命令观察192.168.145.41是否停止发送"A001"消息. 通过这种方法,我们发现关闭了Nagios之后就不会有数据包发送到192.168.145.42的10000端口. 从而缩小了问题范围.
我们进而拿到ambari相应版本的代码,搜索"A001"字符串,发现在"/var/lib/ambari-server/resources/stacks/HDP/2.0.6/services/NAGIOS/package/templates/hadoop-services.cfg.j2"中有一段代码"check_command check_tcp_wrapper_sasl!{{ hive_server_port }}!-w 1 -c 1!A001 AUTHENTICATE ANONYMOUS" 而hive_server_port就是hiveserver2的thrift端口10000. 从 "/var/lib/ambari-server/resources/stacks/HDP/2.0.6/services/NAGIOS/package/templates/hadoop-commands.cfg.j2"得知,nagios用于发送信息检验端口的check_tcp_wrapper_sasl命令最终是通过调用nagios的check_tcp插件完成的, 而check_tcp_wrapper_sasl与check_tcp_wrapper两个命令的不同之处仅在于前者通过check_tcp发送了字符串"A001 AUTHENTICATE ANONYMOUS", 而后者没有发送字符串.
至此,我们可以做出结论,Ambari的Nagios由于监控需要,会定期向hiveserver2发送"A001 AUTHENTICATE ANONYMOUS",但这与hive监听10000的ThriftBinaryCLIService服务的协议不一致(可能是Ambari的Nagios的问题),导致ThriftBinaryCLIService在解析消息时,截取消息第2到第5位以解析出消息长度时解析出了一个超大的整数,用该整数初始化byte数组,从而导致了OOM.
解决方案如下:
1, 在hive-site.xml上配置hive.server2.authentication=NOSASL, 取消hive的SASL认证
2, 将"/var/lib/ambari-server/resources/stacks/HDP/2.0.6/services/NAGIOS/package/templates/hadoop-services.cfg.j2"中的"check_command check_tcp_wrapper_sasl!{{ hive_server_port }}!-w 1 -c 1!A001 AUTHENTICATE ANONYMOUS" 改为 "check_command check_tcp_wrapper!{{ hive_server_port }}!-w 1 -c 1" 即更改Ambari的Nagios的监控脚本模板文件,以通过普通的tcp访问去监控hive,而不发送任何字符串消息.
相关推荐
标题中的"Ambari+HDP+HDP-UTILS.rar"表明这是一份与Apache Ambari、Hortonworks Data Platform (HDP)以及HDP-UTILS相关的压缩包资源。这些组件在大数据处理和管理领域具有重要作用,主要用于简化Hadoop生态系统的...
一定要注意:其他版本如Ambari-2.7.3.0 + HDP-3.1.0.0,Ambari-2.7.4.0+HDP-3.1.4.0,Ambari-2.7.5.0 + HDP-3.1.5.0版本可以私信我。其他版本一定要联系博主,否则下载错误,不负责,链接失效也可以私信我。
这个压缩包"Ambari-2.7.3.0 + HDP-3.1.0.0"包含了Ambari的特定版本以及Hortonworks Data Platform(HDP)的3.1.0.0版本,这是一套全面的数据处理和分析解决方案。 Ambari提供了用户友好的Web界面和API,使得管理员...
以上提供了Ambari不同版本以及HDP不同版本对于多种Linux发行版的支持,包括BaseURL、RepoFile及Tarballmd5|asc等信息,这对于用户在安装配置Hadoop集群时非常有用。根据自己的系统版本选择合适的下载链接,并按照...
总结起来,Ambari-2.7.3.0+HDP-3.1.0.0-Ubuntu的安装包旨在帮助用户在Ubuntu环境中快速搭建和管理Hadoop集群,而"Ambari-2.7.3.0+HDP-3.1.0.0-Ubuntu.txt"文件是实现这一目标的关键资源,其中包含详尽的指导信息。...
在没有网络连接或者网络条件有限的情况下,离线安装是必要的,此时,HDP-UTILS-1.1.0.22-centos7.tar.gz这样的压缩包可以提供必要的依赖和工具。 HDP-UTILS是HDP平台的一部分,包含了多种实用工具,如Hadoop相关的...
集成了hadoop3.2.4,hive3.1.3, spark3.2.1,kyuubi,ozone等组件,完全基于Apache版本使用Ambari2.7.6进行集成,支持centos系的国产操作系统,例如红旗等。同时支持x86和aarch64两种cpu架构,满足国产化改造的需要...
网盘下载包括: ambari-2.7.5.0-centos7.tar.gz HDP-3.1.5.0-centos7-rpm.tar.gz HDP-GPL-3.1.5.0-centos7-gpl.tar.gz HDP-UTILS-1.1.0.22-centos7.tar.gz
标题 "HDP-GPL-3.1.4.0-centos7-gpl.tar.gz" 提供的信息表明,这是一个与HDP(Hortonworks Data Platform)相关的开源版本,版本号为3.1.4.0,是为CentOS 7操作系统设计的。HDP是一个基于Apache Hadoop的全面数据平台...
支持x86_64和ARM64(aarch64)
包含集群部署所需的: ambari-2.7.5.0-centos7.tar.gz HDP-3.1.5.6091-centos7-rpm.tar.gz ...hdf-ambari-mpack-3.4.1.1-4.tar.gz HDP-UTILS-1.1.0.22-centos7.tar.gz jdk-8u112-linux-x64.tar jce_policy-8.zip
《Ambari最新版安装详解:HDP-GPL-3.1.0.0-centos7-gpl.tar.gz深度解析》 在大数据处理领域,Apache Ambari是一款至关重要的工具,它提供了直观的Web界面,使得Hadoop集群的部署、管理和监控变得简单易行。本文将...
目录:Hidataplus->hdp3.1.0 说明:里面还有ubutntu18和centos7两个版本的 按需使用
内容包括ambari-2.7.4.0-centos7.tar.gz,HDP-3.1.4.0-centos7-rpm.tar.gz,链接失效或者部署过程中遇到任何问题可以私信我,同时列举了几条之前项目环境部署过程中遇到过的问题及解决办法,有爱好大数据领域的同学...
安装这些驱动后,笔记本电脑将能够识别并正确操作HDP-III,确保其在实时切换型HDO-IV模式下也能保持稳定的工作状态。实时切换型HDO-IV意味着用户可以在不同的安全级别之间快速切换,无需重启计算机,从而提高了工作...
这个压缩包"Ambari-2.7.4.0+HDP-3.1.4.0安装包-HDP安装包大全"包含了Ambari的2.7.4.0版本和Hortonworks Data Platform(HDP)的3.1.4.0版本,这两个组件都是针对CentOS7和Redhat7操作系统的。HDP是一个全面的企业级...
标题 "HDP-GPL-2.6.4.0-centos7-rpm.tar.gz" 提供的信息表明,这是一个与HDP(Hortonworks Data Platform)相关的开源版本,具体为2.6.4.0版,面向CentOS 7操作系统的RPM(Red Hat Package Manager)格式的软件包。...
现在都收费了,自行下载并保存到网盘确定可用
Hive-release-HDP-3.1.5.0-152-tag.tar.gz文件包含了Hive的源代码,这对于想要构建复杂查询逻辑、优化查询性能或扩展Hive功能的开发人员来说非常有价值。 Spark2是大数据处理的另一个关键组件,它在Hadoop生态系统...
该软件包主要用于在CentOS 7操作...通过安装和配置HDP-GPL-3.0.1.0-centos7-gpl.tar.gz,您可以搭建一个功能强大的大数据平台,用于处理和分析各种类型的数据。它可以帮助您实现数据的存储、处理、查询和可视化等任务。