利用java反射排查一次线上问题(确定问题及问题定位)
背景
hive 用的 1.1.0版本(其实这个版本bug挺多,包括执行计划串列的等等问题吧,建议大家如果选1.x版本用1.2.2吧),一下提到的代码部分如无特殊说明都是hive-1.1.0版本。
前段时间写一个hive sql 预估资源的服务(根据sql返回其读取的行数及所读文件(表)的大小,在运行时给其指定合理资源的大小,目前我们把线上所有hql转到sparksql上执行,所以要指定其资源),其实之前一直利用hiveserver2(以下hiverserver2都用hs2替代)完成。
hs2比较轻量简单,但是hs2服务不是特别稳定,用过的人都知道(包括卡死,对hdfs敏感,对avro类型文件解析也时常有问题)。
经常出现接口服务超时问题。其实有这些问题也可以暂时忍一忍,但是前段时间出现有些sql可以导致整个接口不可用的情况,实在不可忍啦,就决定重写了。然后就掉进坑了啦!
上线问题排查,定位问题sql,排查卡死原因
首先根据后台日志找到了卡死服务的sql,然后就用这个sql debug呗,sql也较为复杂(大概600-700行左右)最后定位到应该是optimizer的问题。
这个优化器大概是做什么的呢,包括join 优化呀,谓词下推呀等等十来种吧(以后我会写一篇hive Driver 执行流程的文章,会详细介绍这部分内容。)
但是为什么他会影响其他sql呢,其实hive 在compile方法加了全局锁。这个锁是Driver 类的一个全局静态锁,所以相当于
private static Object mutex = new Object();
...
synchronized(mutex){
driver.compile(sql)
}
...
这个sql 卡在了 complie中的optimizer中了,死循环了,无法释放mutex锁对象了,然后就崩盘喽。
当时跟老大说了一下这个情况,我的第一反应是继承Driver 重写compile方法解决问题,老大的第一反应是用反射解决。(我后来详细看了下代码,用反射也可以解决,虽然我们要修改的变量不仅是private 还是final的,但是我们把他反射出来改变他的内容还是没有问题的)
然后我就开始尝试先用我的方案解决,继承Driver重写compile,然后发现比较困难,为什么呢,hive源码中用了相当多的私有方法及变量,方法层层调用也较多,如果重写compile就要重写茫茫多的方法及变量,如果升级hive版本也是麻烦事,可能这些个重写都要再写一遍,不是不能实现就是太恶心了。pass
然后我就想到了能不能用停止线程的方式来做,就是这个线程执行时间太长了,把他停止,让他把锁释放掉,让其他线程进来执行任务。
停止线程的方式可以参见我得另一篇文章 --------------------------------------------------------- 这里不多说了。
其实停止线程不是不行(是有间接方案的),但是你要明确知道你代码在哪里死循环出不来了,可以间接通过thread.interrupted()判断 抛异常或者return的方式跳出,但是由于代码不是我们自己写的, 你无法确定所有的sql是不是都在某个optimizer卡住。这要改起来就非常恶心了,到处是判断,而且修改完要重新编译。pass
然后我灵机一动偷偷看了hive1.2.x的实现,发现问题依旧,没有解决。又看了眼hive 2.0.0实现解决了,我凑 我凑 我凑 ,赶紧看看怎么解决的。这可以看我另一篇文章 ---------- 哈哈哈...
然后简单啊,就把pom中hive版本修改到2.0.0 ,在线下测试了3000个sql 没啥问题,上线,一晚相安无事,早上起来发现接口报警了,超时了,我凑,不是解决了吗,怎么回事,登陆服务器一看,full gc严重 old区 去都99%了,full gc 都无法让old对象减少,把堆栈dump出来,估计版本出现大坑了,重启了一下开始排查问题。
如果分析dump出来的堆栈信息 可以看我这篇 https://www.cnblogs.com/jiangxiaoxian/p/9559813.html 文章。
我说结果吧,发现ShutdownHookManager 类的全局变量的set<HookEntry> hookEntry其实是对runnable对象的封装。hive2.0.0实现与 hive1.x不一样,他为了做超时锁,引入了ShutdownHookManager 类,而且用全局的Set维护每个线程对象runnable,每个线程进来都会先remove掉上一个driver 的runnable,然后add添加当前的runnable对象(这里说的不是太准确,我为了简化问题描述吧),但是我的实现是每次new Driver() (driver类成员有runnable 对象), 导致remove不掉上一个driver的runnable对象,现在已经知道问题答案了,看起来挺简单,排查起来还是花了一些时间的。我当时是用反射确定Set <HookEntry> 无法remove的,他的size 一直在增长,具体怎么玩呢。
我是用dump分析定位问题出在哪里,用反射确定问题就在这里!
大家可能看不太懂以下代码,主要是hive 这部分源码变量大家不熟悉,有兴趣可以看下,这里还是比较简单的。
try {
Class<?> clazz = Class.forName("org.apache.hive.common.util.ShutdownHookManager");
// Class<?> clazz = Class.forName("org.apache.hadoop.hive.ql.Driver.class");
// Field hooks = clazz.getDeclaredField("hooks");
Field mgr = clazz.getDeclaredField("MGR");
// hooks.setAccessible(true);
mgr.setAccessible(true);
ShutdownHookManager shutdownHookManager = (ShutdownHookManager) mgr.get(ShutdownHookManager.class);
set = (Set) hooks.get(shutdownHookManager);
System.out.println(set.size)
} catch (Exception e) {
e.printStackTrace();
}
最后的解决方式是其实是用Driver 的destory方法,可以清理掉他自己runnable对象,虽然当时也看到这个方法了,但是是在driver.close()后调用的,没生效,必须在close()他之前调用。close()主要是关闭driver一些相关资源的,destory可以remove掉runnable对象。

文章写得有些啰嗦,主要是想表达我遇到问题整个思考的过程,及解决问题的方式。在此感谢老大则杰在问题排查过程中给的宝贵建议!this`s all
利用java反射排查一次线上问题(确定问题及问题定位)的更多相关文章
- 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换
作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...
- 利用Java反射实现JavaBean对象相同属性复制并初始化目标对象为空的属性的BeanUtils
有时遇到将数据传输对象转换成JSON串会将属性值为空的属性去掉,利用Java反射实现JavaBean对象数据传输对象的相同属性复制并初始化数据传输对象属性为空的属性,然后转换成JSON串 packag ...
- 利用JAVA反射机制设计通用的DAO
利用JAVA反射机制设计一个通用的DAO 反射机制 反射机制指的是程序在运行时能够获取自身的信息.在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息. 反射机制创建类对象 ...
- 利用Java反射机制对实体类的常用操作工具类ObjectUtil
代码: ObjectUtil类: import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.Simpl ...
- 利用Java反射根据类的名称获取属性信息和父类的属性信息
代码: import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java ...
- 利用java反射调用类的的私有方法--转
原文:http://blog.csdn.net/woshinia/article/details/11766567 1,今天和一位朋友谈到父类私有方法的调用问题,本来以为利用反射很轻松就可以实现,因为 ...
- 利用Java反射机制将Bean转成Map
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang ...
- java运维: 一次线上问题排查所引发的思考
本文转载自 crossoverJie 的b博客 https://www.cnblogs.com/crossoverJie/p/9282065.html 前言 之前或多或少分享过一些内存模型.对象创建之 ...
- Java开发必须掌握的线上问题排查命令
作为一个合格的开发人员,不仅要能写得一手还代码,还有一项很重要的技能就是排查问题.这里提到的排查问题不仅仅是在coding的过程中debug等,还包括的就是线上问题的排查.由于在生产环境中,一般没办法 ...
随机推荐
- ubuntu-docker入门到放弃(二)docker初探(基本用法及命令)
一.使用公共镜像 docker有一个开源的镜像管理系统,上面有很多常见的images,如mysql,nginx,tomcat等,可以直接根据自己的需求下载下来用,还有系统images,如redhat, ...
- java.util.ConcurrentModificationException的解决办法
今天在使用iterator.hasNext()操作迭代器的时候,当迭代的对象发生改变,比如插入了新数据,或者有数据被删除. 编译器报出了以下异常: Exception in thread " ...
- STL基础--基本介绍
为什么要使用C++标准库 /* * 为什么使用C++标准库: * 1. 代码重用,不用重新造轮子 * 2. 效率(快速,且使用更少的资源). 现代C++编译器经常对C++标准库的代码有优化 * 3. ...
- elasticsearch 口水篇(4)java客户端 - 原生esClient
上一篇(elasticsearch 口水篇(3)java客户端 - Jest)Jest是第三方客户端,基于REST Api进行调用(httpClient),本篇简单介绍下elasticsearch原生 ...
- Aysnc的异步执行的线程池
ProxyAsyncConfiguration.java源码: @Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public clas ...
- Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素-un
ylbtech-Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素 1.返回顶部 1. Java 实例 - 查找数组中的重复元素 Java 实例 以下实例 ...
- [转]Android 代码自动提示功能
源地址http://blog.sina.com.cn/s/blog_7dbac12501019mbh.html 或者http://blog.csdn.net/longvslove/article/de ...
- Oracle空查询删除
- 各种http报错的报错的状态码的分析
HTTP常见错误 HTTP 错误 400 400 请求出错 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就无法重复此请求. HTTP 错误 401 401.1 未授权:登录失败 此错误表 ...
- git之win安装git和环境配置及常用命令总结
12.windowns安装git和环境变量配置 11.git之常见命令总结 ===== 12.windowns安装git和环境变量配置 ; 转自 https://wuzhuti.cn/2385.htm ...