• 假ArrayList导致的线上事故......


    假ArrayList导致的线上事故…

    线上事故回顾

    • 晚饭时,当我正沉迷于排骨煲肉质鲜嫩,汤汁浓郁时,产研沟通群内发出一条消息,显示用户存在可用劵,但进去劵列表却什么也没有,并附含了一个视频。于是我一边吃了排骨,一边查看消息点开了视频,en~,视频跟描述一样。但没有系统告警,用户界面也没有明显的报错提示,怀疑是小部分特殊情况导致的,查看消息后几秒,我直接被@来处理问题,擦,只好把外卖盒重新盖好,先去处理问题。

    处理经过

    • 通过群内产品发的用户邮箱查到了用户id,再根据接口的相关日志结合uid在日志平台进行关联查询,查到日志后,再拿到traceId进行链路查询,果不其然,发现了异常日志,如下部分日志所示

    • java.lang.UnsupportedOperationException: null
      	at java.util.AbstractList.add(AbstractList.java:148) ~[na:1.8.0_151]
      	at java.util.AbstractList.add(AbstractList.java:108) ~[na:1.8.0_151]
      
      • 1
      • 2
      • 3
    • UnsupportedOperationException是个什么玩意

    • @Slf4j
      @SpringBootTest
      public class Demo {
      
          public void test(Context context) { 
              context.getList().add("皮皮虾");
          }
      
      }
      
      @Data
      class Context {
          
          private List list;
          
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    • 基本操作就是拿到上下文中的List,然后再add一个元素

    • 讲道理,add操作是不会有问题的,有问题的还得是List,追根溯源,让我康康这个List是怎么来的于是我一顿狂点,来到了set这个list的位置

    • @Slf4j
      @SpringBootTest
      public class Demo {
      
          public void test(Context context) {
              context.setList(Arrays.asList("皮皮虾"));
          }
      
      }
      
      @Data
      class Context {
      
          private List list;
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    • context.setList(Arrays.asList("Code皮皮虾")); 这行看起来好像没问题啊Arrays.asList(T... a)我们平时也会用,传入一个数组,返回出一个List没啥问题呀

    • 那我再试试add方法 ,擦,问题复现了,还真是Arrays.asList(T... a)生成的List的add方法报错,由于线上存在问题,则先修改为以下代码上线,也就是修改为我们平时正常的写法, 上线后,观察了下日志,群里回复已解决问题,也让用户重试,发现没问题,自此问题解决。

    追根溯源

    • 进入asList方法,发现底层new了一个ArrayList,并将数组传入作为List的元素

    • @SafeVarargs
      @SuppressWarnings("varargs")
      public static  List asList(T... a) {
          return new ArrayList<>(a);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • emm,看起来很简单啊,没问题啊,咋会报错呢?别着急,咱们在点开这个ArrayList瞅瞅

    • private static class ArrayList extends AbstractList
          implements RandomAccess, java.io.Serializable
      {
          private static final long serialVersionUID = -2764017481108945198L;
          private final E[] a;
      
          ArrayList(E[] array) {
              a = Objects.requireNonNull(array);
          }
          // ... 省略
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 擦,这ArrayListArrays类的一个静态内部类,不是我们经常用的java.util.ArrayList 继续看,这个静态内部类ArrayList继承了AbstractList,而且默认是没有实现add方法的.也就是说调用add方法会直接调用父类,也就是AbstractListadd方法,源码点开一看,真相大白了.AbstractListadd方法直接抛出UnsupportedOperationException异常,跟线上报错一模一样!!

  • 相关阅读:
    git reset 与 git revert 用法(回退远程提交)
    【Spring Cloud】新闻头条微服务项目:引入ElasticSearch建立文章搜索索引
    spring boot加mybatis puls实现,在新增/修改时,对某些字段进行处理,使用的@TableField()或者AOP @Before
    MySQL笔记1(数据库的好处,数据库的概念,数据库的特点,MySQL的启动,数据模型,SQL)
    电源方案对比
    resultMap结果映射
    爽爆了!字节架构师纯手打Java技术小册(故事版)开源分享
    docker基本管理
    Java内存区域与内存溢出异常(1)
    项目管理之常见七大问题挑战
  • 原文地址:https://blog.csdn.net/Andrew_Chenwq/article/details/134433114