背景

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. Java ArrayList排序方法详解

    由于其功能性和灵活性,ArrayList是 Java 集合框架中使用最为普遍的集合类之一.ArrayList 是一种 List 实现,它的内部用一个动态数组来存储元素,因此 ArrayList 能够在 ...

  2. centos 磁盘清理 /dev/vda1系统盘满了

    df   -h   检查一台服务器磁盘使用空间,发现磁盘已经使用了100% 思路是: 1.cd /usr   当然这里不一定是/usr目录,最好是cd到 根目录再执行下一步 2.du -sh * 看哪 ...

  3. 关于RabbitMQ分布式集群架构

    RabbitMQ分布式集群架构和高可用性(HA) (一) 功能和原理 设计集群的目的 允许消费者和生产者在RabbitMQ节点崩溃的情况下继续运行 通过增加更多的节点来扩展消息通信的吞吐量 1 集群配 ...

  4. 经典面试题目——找到第n个丑数(参考《剑指offer(第二版)》面试题49)

    一.题目大意 给你一个数n,要求返回第n个丑数.其中,丑数的定义如下: 丑数是指只包含因子2.3和5的数.(数字1也是丑数,不过是个特例)引用<剑指offer>上的话来说,对于一个数M,如 ...

  5. awk中使用shell的环境变量

    awk中使用shell的环境变量一:"'$var'"这种写法大家无需改变用'括起awk程序的习惯,是老外常用的写法.如:var="test"awk 'BEGIN ...

  6. 【Darwin学习笔记】之获取系统处理器数量的方法

    阅读Darwin源码的时候看到这个方法,感觉挺有用处,且考虑了多种平台下的实现方式,直接贴代码,以后说不定会用到~ 单一种平台下的实现方法可能很容易,但是把这些个系统都收集在一起,在一个函数中实现还是 ...

  7. Android开发之Activity生命周期篇

    一.Activity: 1.Activity:Activity是一个与用记交互的系统模块,几乎所有的Activity都是和用户进行交互的. 2.在Android中Activity主要是用来做控制的,它 ...

  8. java高并发编程(四)高并发的一些容器

    摘抄自马士兵java并发视频课程: 一.需求背景: 有N张火车票,每张票都有一个编号,同时有10个窗口对外售票, 请写一个模拟程序. 分析下面的程序可能会产生哪些问题?重复销售?超量销售? /** * ...

  9. JSON: 介绍、应用

    ylbtech-JSON:  介绍.应用 JSONP(JSON with Padding)是 JSON 的一种“使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据. 为什么我们从不同的 ...

  10. C# 读写 ini 配置文件

    虽说 XML 文件越发流行,但精简的 ini 配置文件还是经常会用到,在此留个脚印. 当然,文中只是调用系统API,不会报错,如有必要,也可以直接以流形式读取 ini文件并解析. /// <su ...