这个是本系列的第三个文章,主要用于记录一下几个边缘业务的线上问题/核心业务的小问题的排查方案与解决
基本上排查思路就是技术保证基础参数的排查诊断,业务能力保证排查的流程思路是清晰的,精确找到对应出问题的业务节点进行解决。
我所在的业务组,除了一些关键业务我没有参与外(我参与了云存储和告警)其他的像是 webRTC,信息平台,cdn 这些都算是小业务点,这些有一些也有线上问题
系列文章见 【实践】2025年线上问题解决与总结-1
【实践】2025年线上问题解决与总结-2
三元运算符NPE问题
背景
#Java #TRAELAND环境上线发生 NPE 报错,造成紧急回退
有以下两方面需要查看
若 connectMode.contains(1)报 NPE,代表需要查看 stream 运算是否返回 nullproductDocDO.getApScanCapacity()可能会有三元运算符的自动拆装箱产生的 NPE
根因分析
触发了自动拆装箱,但是 Integer 对象是 null,触发了 NPE 开发时在环境中配置了 MySQL 字段的非空默认值,但是测试完沟通后直接将默认值删除,没有验证没有默认值的情况,造成了 NPE
- stream操作是否会引起NPE或者返回null对象?
- Arrays.stream(…):将数组转换为流。不会返回 null。productDocDO.getConnectMode():如果这个方法返回 null 或者 productDocDO 是 null,那么整个表达式将会抛出 NullPointerException。.split(SEMIcolon):将字符串按分号分割成数组。如果结果是空字符串(””),则会生成一个包含单个空字符串的数组([“”])。.map(Integer::parseInt):将每个字符串转换为整数。如果遇到非数字字符串,会抛出 NumberFormatException。.collect(Collectors.toList()):将流中的元素收集到一个列表中。不会返回 null。
- 要看stream是否返回一个null对象,则可以直接看stream流表达式中的最后一个操作,也就是输出操作,其他的可以不用关注,stream流详细设置可以单独写一篇文档进行讲解
代表 stream 流不会返回 null 对象,信息来源 Collectors(Java Platform SE 8) 。
- Collect.toList()方法只会返回一个新的List对象,因此使用stream流开发时不需要考虑会产生null对象,但是stream操作过程中可能会产生NPE或其他问题
三元运算符是否会引起 NPE?
三元运算符在使用时会自动触发数据类型的拆装箱,如会将 Integer 变成 int 类型,如果对象数据类型是 null,在拆装箱时会出现 NPE 问题
信息来源
自动拆装箱的原因为三目运算符两侧数据类型不一样时,为了确保信息一致,会进行拆装箱,这是为了确保三目运算符的结果类型一致。
信息来源 docs.oracle.com/javase/spec…
解决方案
三目运算符两侧类型保持一致/非空校验或填充默认值
- 不要在测试完成后随意修改一切相关文本,包括但不限于代码、表结构、配置文件信息等,如果修改则代表需要重新进行测试!
- 开发过程中如果使用基本数据类型,在调用链路中(如函数嵌套,对象在多个语句中等)需要尽量避免自动拆装箱三元运算符,要么冒号两侧类型是相同类型,如都是Integer或者都是int,或者是对象类型使用Optional进行包装
- 不太熟的API使用前需要详细查看使用时容易出现的问题
其他的小bug
- 【云存储】云存储套餐状态下发,有时会有状态异常,通过定时任务,对设备状态和套餐状态进行对比,发现异常
原因是 kafka 下发乱序,比如我们这边有业务是通过 kafka 先下发关闭再下发开启,如果乱序,最终状态时关闭,与实际业务不符合,通过把 deviceid 作为 kafka 消息的 key+粘性分区
后续解决方案是对于顺序性要求高的,重要的业务,一律使用 key+粘性分区
- 【云存储】云存储有时候有状态问题,造成脏数据(比如开启的套餐触发归档流程)
解决方案是 通过幂等+状态机,解决该问题
通过严格的状态调整,避免异常状态转换,避免后续脏数据的人工介入
- 【告警】告警的入库率有问题,也有重复
因为主要是通过 kafka 进行消费,网关性能较好暂未关注,所以需要保证 kafka 生产->broker 存储->consumer 消费->进行业务处理->存储数据库 这个链路不会有重复或者丢失
- 【云存储】批处理只有一个pod执行,且长时间执行,中途任务中断会导致进度丢失,影响业务
解决方案:一个是对批处理任务进行进行存储,按照业务点(如我这边进行套餐的状态更新、将套餐和设备进行校验,基准是套餐,所以可以通过单次查询1000个套餐,进行业务处理),每完成一个小型批处理任务则可以往数据库中记录 任务,处理量,完成时间,任务对应日期 等信息,这样重启尝试重新查看是否有遗漏时,可以直接通过这张表即时回溯,快速恢复到任务失败之前的状态
为了解决多个 pod 进行批处理业务,通过 redisson 锁+偏移量实现,redisson 表示记录偏移量时只能有一个 pod 修改,偏移量代表这个长时间任务的执行进度
