一个枚举类的set方法引发的血案

事故回顾:

   线上集群环境,其中一台机器出现了诡异的现象:明明rpc接口响应的结果是成功,但是调用方判断的时候走的是失败的判断逻辑,导致持续的报警,并且只有一台机器有着个现象,先用重启大法解决了,具体原因是什么?一堆工程师开始分析。

分析原因:

   查日志,在某个时间点之后开始大量报错,大家开始围在一起讨论:

   这个时间有没有新上线?没有。

   有没有可能jar包不正确?不可能,代码已经有几天没更新了。

   甚至想到了黑客攻击?更不可能。

   再回到原来的问题上,响应结果和判断结果的逻辑都是根据枚举的值,那么看看枚举值是否正确?代码没问题。明明正确,为什    么运行时不正确?

   运行时不正确的唯一原因就是运行时被修改了,那么怎么修改呢,set方法!枚举中的set方法!恍然大悟,赶紧去看看代码有没有这个set方法,果然有,并且还有调用的地方,这个调用地方的代码平常很少被执行到,所以以前一直没有报出来,今天某个时间点只有这台机器的代码被执行了,所以 这台机器开始上演诡异的报警。

  OK,问题被找到了,修改代码,重新上线,下边分析下这个枚举类。

 枚举类:

  枚举类是jdk的一个语法糖,其本质是通过普通类实现的,只是编译器为我们进行了加工处理,每个枚举类型编译后的字节码实质都是继承自Java.lang.enum的枚举类型同名普通类,而每个枚举常量实质上是一个枚举类型同名普通类的静态常量对象,所有枚举常量都是通过静态代码块进行初始化实例赋值。

public enum status{
 
 start("a"),running("b"),stop();
}

我们对如上的枚举类型进行javac编译后通过Javaap -v Status.class 可以查看其编译后的字节码如下:

public final class Status extends java.lang.Enum<status>
{
public static final Status START;
public static final Status RUNNING;

public static final Status STOP;

.....


}

其实写到这里,这个问题出现的原因很清楚了,枚举本质是被编译器处理成类,枚举值是静态的常量属性,枚举只是一种语法糖,被编译器生成最终的类。从某种意义上可以说jdk1.5后引入的枚举类型是枚举常量类的代码封装而已。当用set方法进行赋值的时候,实际上是修改的一个内存中的静态变量的值,这个值原本的意义就被修改了,这时候如果其他地方再来判断,必然出错,一场血案就此发生!


   

猜你喜欢

转载自blog.csdn.net/wzbwzh/article/details/80837570