事故回顾:
线上集群环境,其中一台机器出现了诡异的现象:明明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
{
public static final Status START;
public static final Status RUNNING;
public static final Status STOP;
.....}
【一个枚举类的set方法引发的血案】其实写到这里,这个问题出现的原因很清楚了,枚举本质是被编译器处理成类,枚举值是静态的常量属性,枚举只是一种语法糖,被编译器生成最终的类。从某种意义上可以说jdk1.5后引入的枚举类型是枚举常量类的代码封装而已。当用set方法进行赋值的时候,实际上是修改的一个内存中的静态变量的值,这个值原本的意义就被修改了,这时候如果其他地方再来判断,必然出错,一场血案就此发生!
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)