利用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等,还包括的就是线上问题的排查.由于在生产环境中,一般没办法 ...
随机推荐
- Docker Dockerfile 一
1.概述 创建Docker镜像的方式有三种 docker commit命令:由容器生成镜像: Dockerfile文件+docker build命令: 从本地文件系统导入:OpenVZ的模板. 关于这 ...
- Object类有哪些方法
Object是所有类的父类,任何类都默认继承Object.Object类到底实现了哪些方法? 1.clone方法 保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出 ...
- Hadoop是怎么分块Block的?
不多说,直接上干货! hadoop的分块有两部分. 第一部分就是数据的划分(即把File划分成Block),这个是物理上真真实实的进行了划分,数据文件上传到HDFS里的时候,需要划分成一块一块,每块的 ...
- tomcat中class和jar的加载顺序(转)
https://blog.csdn.net/lipei1220/article/details/53924799 加载顺序: 1. $java_home/lib 目录下的java核心api 2. $j ...
- Java注解的基本概念和原理及其简单实用
一.注解的基本概念和原理及其简单实用 注解(Annotation)提供了一种安全的类似注释的机制,为我们在代码中添加信息提供了一种形式化得方法,使我们可以在稍后某个时刻方便的使用这些数据(通过解析 ...
- Mybatis 系列1-环境搭建
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- android Keycode 完全对照表
input keyevent 82 menuinput keyevent 3 homeinput keyevent 19 upinput keyevent 20 downinput keyevent ...
- Android Studio将项目打包成apk
Android Studio将项目打包成apk 第一种方法:适合自己调试用. (1)直接在项目中生成: (2)位置是在你的项目中 第二种方法:适合发布应用. (1)找到Generate Signed ...
- C语言格式化%整理
以输出为例: #include <stdio.h> main() { printf("**进制****************************************** ...
- Android 接入X5WebView,让WebView加载更快;
X5内核,微信和QQ浏览器都在用的WebView: 官网地址:https://x5.tencent.com,详细的信息进官网了解: 这是官方的宣传语: 1) 速度快:相比系统webview的网页打开速 ...