12月知识小报|线上问题的抽丝剥茧与一锤定音

news2024/11/15 16:54:32

海恩法则是德国飞机涡轮机的发明者帕布斯·海恩提出的一个在航空界关于飞行安全的法则。

每一起严重事故的背后,必然有29次轻微事故和300起未遂先兆以及1000起事故隐患。

作为开发者,安全生产是我们底线,敬畏每一行代码,挖掘每一个故障背后的根因,避免再次落入同一个坑是应该成为我们的工作习惯,这里有几个我们线上真实有趣的故障案例分享给大家。

三目运算符--最熟悉的陌生人

三目运算符任何一个初学JAVA的同学都会接触到,而且在我们代码中也处处可见:

expression1 ? expression2 : expression3

expression1 可以是计算为 boolean 值的任何表达式。如果 expression1 是 true ,那么将评估 expression2 。否则,将评估 expression3。然而就是这个最熟悉的陌生人给我们带来一次可怕的线上故障,故障过程略去不表,大家可以先看一段代码,盲猜一下哪里出了问题:

77c60b1fe8c31257d1fa6eecca5ed58e.png

第一眼可能可能就能看出,最后一行三目运算法的 null != playBoyExtra貌似是多余的,因为前面已经判空并且return了啊。 

是的,这个的确是个问题,但是不至于执行不下去,再看一下错误日志:三元运算符这一行竟然出现万恶的NPE。

49a9683e222220ce7dcd473605e58aab.png

为何这里会出现NPE,唯一有可能为null的只能是playBoyExtra.getOpenTime() == null,但是为何会抛出NPE不应该是三目运算结果是null?写个简单的测试代码:

public class ConditionalOperatorDemo {
    public static void main(String[] args) {
        PlayBoyExtra playBoyExtra = new PlayBoyExtra();
        Long openTime = null != playBoyExtra ? playBoyExtra.getOpenTime():0L;
    }

    private static class PlayBoyExtra{
        private Long openTime;

        public Long getOpenTime() {
            return openTime;
        }

        public void setOpenTime(Long openTime) {
            this.openTime = openTime;
        }
    }
}

用javap -c 对测试代码进行反编译:

1b0e7333501e66f09533d706ae19c93a.png

观察红框内的三行,这三行翻译过来其实等同于:

Long openTime = (Long)(null != playBoyExtra ? playBoyExtra.getOpenTime().longValue():0L);

第一步:执行playBoyExtra.getOpenTime()获取到一个Long类型的对象O1,然后这里在某些情况下返回的是null。 

第二步:执行O1.longValue()得到long基本类型数值V2,也就是执行一个自动拆箱操作,在playBoyExtra.getOpenTime()返回null的情况下就变成了null.longValue(),就会抛出NPE。 

第三步:执行三目运算,并且将三目运算结果long类型的V3进行自动装箱操作,Long openTime=(Long)V3。 

为何会执行自动拆箱呢?这其实是三目运算符的语法规范。参考JLS-15.25.2 Conditional Operator ? : 章节【附录1】的描述:

8a52b63e6f56db739bdf3965ca3184c3.png

如红框所言,如果第二和第三位操作一个是基本类型,一个是对象,那么会发生自动拆箱操作转换为基本类型。这里就是因为第三个值是基本类型“0L”,所以导致出现playBoyExtra.getOpenTime()会发生自动拆箱操作,而当playBoyExtra.getOpenTime().longValue()值为null的时候,null.longValue()就抛出NPE,了解原理,再实验一下,直接把“0L”改为包装类型是不是就不会发生自动拆箱操作就不会抛出异常了呢?

Long openTime = null != playBoyExtra ? playBoyExtra.getOpenTime():Long.valueOf(0L);

验证一下,果然如此,实际上openTime被赋予了null值,程序执行并没有报错,但是这里虽然程序执行没有报错,但是这个结果显然不是程序的本意,程序本意是如果playBoyExtra.getOpenTime()不到值,那么就初始化openTime为0L,也就是我们更习惯的写法是:

Long openTime = null != playBoyExtra.getOpenTime() ? playBoyExtra.getOpenTime():Long.valueOf(0L);

至于playBoyExtra本身是否为空,其实前面已经判断过了。至此,事情水落石出,三目运算符是不是有一种“最熟悉的陌生人”的感觉?

消息风暴--好心办坏事

2022年3月30号晚7点25分,一阵急促的告警电话袭来,负责同学迅速查看系统,发现数据库连接池被同一个UPADATE的SQL语句产生行锁,导致连接池占满,应用系统无法访问数据库,线上大量系统异常。

5d6d06be46af146d3b49bb465f900a97.png

数据库相关的问题往往容易引发灾难性的重大问题,好在告警及时,迅速对这条SQL语句进行限流,DB连接池恢复,线上危机解除,但是必须找到根因才能算真正解除这个定时炸弹。线上代码变更在大多数情况下是引发故障的主要原因,通过最近的线上代码变更排查,日志排查,代码review等系列问题定位手段,最终发现问题出现一行代码上,下面是这行代码CR的截图:

66a86400765f7654ca4cc65d6002e784.png

粉红色部分表明是被删除的代码,绿色部分是更新后的代,他的核心差异是调用这个消息发送接口的时候,第二个和第三个参数进行了调换,为何会进行调换呢?让我们看一下producerManager.sendMessage方法的定义:

public boolean sendMessage(String topic, String tags, String message) {
        if (message == null || tags == null) {
            log.error("[MetaProducerManager] message|tags is null");
            return false;
        }
        return sendMessage(new Message(topic, tags, message.getBytes()));
}

这是对RocketMQ消息中间件的发送接口的封装,三个核心入参: 

topic:消息队列的topic,订阅者可以订阅特定topic的消息。

tags:消息队列细分tag,订阅者可以基于tags部分订阅此topic的消息。

massage:实际要发送消息body的内容。 

如果看过这个定义,大概就知道为何开发同学要修改原来的那行代码了,因为之前是把messageBody当做tags参数,而TAG_EDIT标识则传给了message消息提了,这是一个显而易见的参数传递错误的BUG,我们这位细心的程序员在开发其他代码的时候,看到了这个如此显而易见的bug,顺手就修复了,然而这次修复,却带来了灾难性的后果。可以用下面这个图示意理解一下:

9076b5847977ac89d7913b212a0527bd.png

在分布式架构下,应用之间常常通过RPC远程调用或者消息订阅的方式相互通信,这里应用A和应用B存在应用B对应用A的数据库变更的RocketMQ消息订阅,消息消费后,在某些特殊数据的处理逻辑下下会产生对于应用A的数据库UPDATE接口的同步RPC调用,而一旦产生update数据库的操作,又会产生数据库变更的RocketMQ消息,这时候应用A和B之间就产生了循环调用直至数据库被update操作hang住,为何原来不会出现这种情况呢,很简单,因为原来BUG的存在,导致红色的消息订阅链路是失效的,也就是根本没有办法正确消费消息,因此这个循环无法建立,当这个BUG被修正的时候,终于消息风暴降临。 

当然,其中有很多细节,就不一一表述,但是这种看到一个BUG,随手修掉的情况,真可谓“好心办坏事”。在面临一个复杂系统的时候,哪怕是修复一个显而易见的BUG,都要慎之又慎,必须要对整体链路和架构有充分判断和认知,才能尽量避免踩到前人的大坑中^_^。

页面卡顿--不一定是代码的锅

某个客户端页面,偶尔有用户舆情反馈页面会出现卡顿,客户端同学开展一系列排查工作,甚至直接联系用户,用完全一样的机型,相同版本,同样网络环境..各种手段都上了,然而始终无法复现,百思不得其解的情况下,拉服务端同学一起排查,然而一样一无所获,从服务端监控上看,接口RT非常稳定,几乎没有抖动,甚至没有用户相关的任何异常日志。直到某一天,忽然发现,似乎有问题的用户请求都落入到一个相同的EA119的机房,赶紧进行模拟验证:

476b54e777d62291fd08b4e6ae6da658.png

果不其然,几十倍的响应时长的差异!而要了解差异的来源,又免不了各种复杂分析,也略去不表,问题的根因的确不在于代码,而是网络请求链路的差异,下图是出问题的时候网络链路。

2b387358ba3a590ebd974d94c6ec20b1.png

我们为了系统容灾诉求,业务应用会进行多机房容灾部署,也就是业务服务器会部署在张北和南通等多个机房,但是在上图这个网络架构中,我们会发现客户端请求都会先到张北的Aserver(网络接入层),如果识别到是需要请求到南通机房的,会再进行一次转发到南通,而单元机房之间的网络传输必然带来较大的RT,当网络优化后,需要路由到南通机房的用户的网络请求直接请求南通机房的Aserver(网络接入层)以后,如下图所示:

631d32a591bb2a1d7cd99a7c98b22ced.png

整个世界都清净了! 

无论是客户端还是服务端,虽然没有修改一行代码,但是这个问题的解决时间那是相当长,包括问题根因排查也是耗时颇久,有时候,未必是代码的锅,程序员如果要成长为优秀的架构师,不仅对业务架构要了解,很多时候也需要对网络架构等更广泛的技术领域有一定的理解,才能在众多复杂问题中抽丝剥茧,一锤定音。 

PS:可能读者有个疑问,为何服务端监控看不到南通机房RT异常呢,原因很简单,服务端RT监控是应用层监控,也就是只监控了请求从进入业务应用到离开业务应用的时长,而真正耗时的是Aserver和业务应用间的时长。

后记

这三个问题都是线上真实发生的问题,从最基础的三目运算符,到相对复杂的分布式系统依赖和调用,再到更复杂的端到端网络架构,每一步都是程序员到架构师的修炼之路,在这技术日新月异的时代,保持强烈技术好奇心和持续学习的良好习惯,相信日积硅步足以致千里。如果能够完整读完这篇文章的你也有一点点收获,那么祝贺你又进步了一点点,顺祝您新年快乐,在新的一年里,兔氣揚眉,前兔似錦!

附录1:JSL 15.25:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25

c93af770d1bf80b85ef729e96562d1d7.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/171885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LINUX学习之文件基本属性(二)

查看文件属性 Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位并拥有不同的权限。在 Linux 系统中,通常使用 chown 命令来修改文件或目录的所有者,chmod 命令则用于设置用户的权限。 chown (change owner) :用于修改…

Linux下的进程通信之system V共享内存

目录 使用system V共享内存进行进程间通信: 获取共享内存shmget 将共享内存关联到进程 去关联共享内存 删除共享内存 简易模拟实现server和client之间的通信: 服务端代码: 客户端代码: 共享内存的特点: 其他…

solrCloud一:zookeeper集群搭建

SolrCloud是基于Solr和Zookeeper的分布式搜索方案,它的主要思想是使用Zookeeper作为集群的配置信息中心。SolrCloud是Solr的一种分布式部署方式 ,当索引越来越大时,一个单一的系统无法满足空间和查询效率上的要求,这个时候往往需要…

SpringCloud(11):Hystrix请求合并

1 简介 如图,多个客户端发送请求调用(消费者)项目中的findOne方法,这时候在这个项目中的线程池中会发申请与请求数量相同的线程数,对EurekaServiceProvider(服务提供者)的getUserById方法发起调用,每个线程都要调用一次&#xff0…

圆满落幕!56 人参加,龙蜥社区技术委员会、运营委员会会议顺利完成

1 月 13 日,龙蜥社区分别召开了第 10 次技术委员会会议和第 14 次运营委员会会议,来自 21 家理事单位的委员代表出席。两个会上分别总结和回顾了龙蜥社区 2022 年度整体技术和运营发展情况,就社区产品、重要技术决策、社区治理、2023 年度运营…

塔望3W消费战略全案丨火出天际的预制菜,能否拯救开饭焦虑?

2022年6月塔望咨询开设塔望食品大健康消费研究院(简称塔望食研院)栏目,塔望食研院以“为食品行业品牌高质量发展赋能”为理念,将不定期发布食品大健康行业研究、消费研究报告。塔望食研院致力于结合外部数据、消费调研数据、企业内…

Web(九)

Web服务器软件Tomcat web服务器软件: 服务器:安装了服务器软件的计算机 服务器软件:接收用户的请求,处理请求,做出响应 web服务器软件:接收用户的请求,处理请求,做出响应。 在…

说话人识别损失函数的PyTorch实现与代码解读

概述 说话人识别中的损失函数分为基于多类别分类的损失函数,和端到端的损失函数(也叫基于度量学习的损失函数),关于这些损失函数的理论部分,可参考说话人识别中的损失函数本文主要关注这些损失函数的实现,…

SQL 分组条件深入剖析

问题在 stackoverflow 网站上看到这样一个 SQL 分组条件的需求,需求看似挺简单,但能把 SQL 写正确对于新手来说也不容易,我们拿过来深入剖析一下,数据如下:需求是查找只有Ready 状态的设备。解答自然思路:按…

寅辞旧岁,卯定常虹丨ASKO洗碗机“净”护新春团圆时刻

农历新年是一年中最重要的节日,但过去三年的特殊时光阻碍了很多人的归乡之行,如今当阴霾逐渐散去,必然会引来大规模的新年归乡潮,奔赴一个久违的团圆年。美馔佳宴是新春佳节的永恒命题,新年家里少不了亲友的光临&#…

Windows 7的最后一个版本

前天推送的文章介绍了,在1月10日微软发布了最后一个补丁后,微软为Windows7提供的所有更新(包括收费的ESU)已经完全终止。以后再有新的补丁,则为第三方平台提供的非官方版的了。 早在2022年9月份,微软就发布了支持安全启动UEFI的补…

DocPrompt代码实现与模型微调

数据预处理阶段 PaddleOCR PP-Structure:这个库其实是用于版面分析的一个开源库,参见:github: Layout-Parser/layout-parserhttps://github.com/Layout-Parser/layout-parser 代码推理阶段 Paddle-Inferencehttps://paddle-inference.readt…

图形编辑器:工具管理和切换

大家好,我是前端西瓜哥。今天我们看看对于一款图形编辑器,应该怎么去实现工具,比如绘制矩形、选中工具,以及如何去管理它们的。 项目地址,欢迎 star: https://github.com/F-star/suika 线上体验&#xff1a…

【改进篇】Python实现VRP常见求解算法——蚁群算法(ACO)

基于python语言,实现经典蚁群算法(ACO)对车辆路径规划问题(CVRP)进行求解, 优化代码结构,改进Split函数 目录往期优质资源1. 适用场景2. 改进效果对比2.1实验结果2.2 改进前后算法性能对比3. 求…

臻图信息构建数字孪生港口船舶停靠管理系统,赋能港口创新发展

我国的港口不仅是船只停靠的避风港,也是现代渔业发展和管理的中心。随着国内港口业的不断发展,国务院在《现代综合运输体系发展“十四五”规划》中提出,要自动化、数字化、智能化等技术来完善监管体系建设。 ​ 随着科技兴港战略的提出&…

“零”代码改动,静态编译让太乙Stable Diffusion推理速度翻倍

作者|梁德澎 AI 作图领域的工具一直不尽人意,直到去年 8 月 Stable Diffusion 开源,成为AI 图像生成领域无可争辩的划时代模型。 为了提升其推理效率,OneFlow 首度将 Stable Diffusion 模型加速至“一秒出图”时代,极…

2023牛客寒假算法基础集训营2(11/12)

Tokitsukaze and abn (easy)Tokitsukaze and abn (medium)要使abn&#xff0c;那么转换一下就是bn-a&#xff0c;所以只需要计算[n-L,n-R]和[L,R]相交的部分即可AC代码&#xff1a;#include <bits/stdc.h> using namespace std; using LL long long; int main() {ios::s…

6. 基本数据类型

1. Python 中的变量不需要声明 每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。 counter 100 # 整型 mile…

关于Win11打开文档总是提示“选择一个程序打开”的问题

这边异常情况&#xff1a; 使用的360浏览器下载回来的文档、微信下载回来的文档都会出现标题所说的问题。 问题产生的原因&#xff1a; 初期在重装电脑后&#xff0c;将自带的一些安装系统后的第三方软件卸载掉了&#xff0c;也包括QQ浏览器。 可是在win11默认应用中看到了…

第五章SpringFramework之AOP

文章目录AOP概念及相关术语概述为什要用 AOP相关术语横切关注点Advice通知Join Point连接点Point CUT 切入点切面目标代理基于注解的SpringAOP准备工作切入点表达式语法重用切入点表达式对应的切面前置通知返回通知异常通知后置通知环绕通知切面的优先级总结Spring AOP 的实现步…