背景

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反射排查一次线上问题(确定问题及问题定位)的更多相关文章

  1. 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

    作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...

  2. 利用Java反射实现JavaBean对象相同属性复制并初始化目标对象为空的属性的BeanUtils

    有时遇到将数据传输对象转换成JSON串会将属性值为空的属性去掉,利用Java反射实现JavaBean对象数据传输对象的相同属性复制并初始化数据传输对象属性为空的属性,然后转换成JSON串 packag ...

  3. 利用JAVA反射机制设计通用的DAO

    利用JAVA反射机制设计一个通用的DAO 反射机制 反射机制指的是程序在运行时能够获取自身的信息.在java中,只要给定类的名字,    那么就可以通过反射机制来获得类的所有信息. 反射机制创建类对象 ...

  4. 利用Java反射机制对实体类的常用操作工具类ObjectUtil

    代码: ObjectUtil类: import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.Simpl ...

  5. 利用Java反射根据类的名称获取属性信息和父类的属性信息

    代码: import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java ...

  6. 利用java反射调用类的的私有方法--转

    原文:http://blog.csdn.net/woshinia/article/details/11766567 1,今天和一位朋友谈到父类私有方法的调用问题,本来以为利用反射很轻松就可以实现,因为 ...

  7. 利用Java反射机制将Bean转成Map

    import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang ...

  8. java运维: 一次线上问题排查所引发的思考

    本文转载自 crossoverJie 的b博客 https://www.cnblogs.com/crossoverJie/p/9282065.html 前言 之前或多或少分享过一些内存模型.对象创建之 ...

  9. Java开发必须掌握的线上问题排查命令

    作为一个合格的开发人员,不仅要能写得一手还代码,还有一项很重要的技能就是排查问题.这里提到的排查问题不仅仅是在coding的过程中debug等,还包括的就是线上问题的排查.由于在生产环境中,一般没办法 ...

随机推荐

  1. 修改docker容器的端口映射

    大家都知道docker run可以指定端口映射,但是容器一旦生成,就没有一个命令可以直接修改.通常间接的办法是,保存镜像,再创建一个新的容器,在创建时指定新的端口映射. 有没有办法不保存镜像而直接修改 ...

  2. WPF实现打印用户界面功能2

    帮助类: using System; using System.Drawing.Printing; using System.IO; using System.Windows.Forms; names ...

  3. [视频播放] M3U8文件格式说明

    M3U文件中可以包含多个tag,每个tag的功能和属性如下: #EXTM3U 每个M3U文件第一行必须是这个tag,请标示作用 #EXT-X-MEDIA-SEQUENCE:140651513 每一个m ...

  4. QT使用SQLite

    在QT的widget中用tableview显示sqlite数据库表中的内容. 用QTcreator创建一个基于Widget类的窗口,再拖一个tableview到widget中,保存. 1.在widge ...

  5. 【windows】之查看端口占用

    打开cmd界面 netstat -aon|findstr "80" 查看80端口占用PIDtasklist|findstr "2448" 找到占用程序直接杀死( ...

  6. 【IIS错误】IIS各种错误

    IIS简介 当用户试图通过HTTP或文件传输协议(FTP)访问一台正在运行Internet信息服务 (IIS)的服务器上的内容时,IIS返回一个表示该请求的状态的数字代码.该状态代码 记录在IIS日志 ...

  7. java设计模式-Observe

    一.背景 请模拟下面情形: 小孩在睡觉,醒来后要求吃东西   代码: class Child{ private boolean wakenUp = false; void wakeUp(){ wake ...

  8. PAT 乙级 1049 数列的片段和(20) C++版

    1049. 数列的片段和(20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CAO, Peng 给定一个正数数列,我们可以从中截 ...

  9. jqgrid使用(1)生成表格

    1.引入js,css 2,基本配置 function init() { $("#list1").jqGrid({ url: "../Listing.ashx", ...

  10. AsyncHelper

    http://www.cnblogs.com/zhaopei/p/async_one.html