现象

rocketmq-common-4.9.3 版本作为客户端消费数据时,从MessageExt.getMsgId()获取消息ID时,会存在一个潜在的依赖冲突问题,最终导致方法执行失败,如下图:

从现象来看,Could not initialize class org.apache.rocketmq.common.message.MessageClientIDSetter 表示 MessageClientIDSetter类为正确被加载,通过搜索类似的资料找到了以下文章:

  1. Client dependency conflict, cause NoClassDefFoundError. Require for shaded client
  2. 记一次RocketMQ消息消费异常

从51cto的文章来看,基本可以分析出是MessageClientIDSetter的静态代码块在执行时异常导致类加载器加载类失败。

static {
        byte[] ip;
        try {
            // 执行异常,导致`MessageClientIDSetter`未成功被类加载器加载
            ip = UtilAll.getIP();
        } catch (Exception e) {
            ip = createFakeIP();
        }
        LEN = ip.length + 2 + 4 + 4 + 2;
        ByteBuffer tempBuffer = ByteBuffer.allocate(ip.length + 2 + 4);
        tempBuffer.put(ip);
        tempBuffer.putShort((short) UtilAll.getPid());
        tempBuffer.putInt(MessageClientIDSetter.class.getClassLoader().hashCode());
        FIX_STRING = UtilAll.bytes2string(tempBuffer.array()).toCharArray();
        setStartTime(System.currentTimeMillis());
        COUNTER = new AtomicInteger(0);
    }

分析

从上面的博客中已经可以得到结论是依赖冲突造成的,但问题是部分服务会出现,大多数服务不会出现,通过往深层次分析得出结论
rocketmq-acl rocketmq-client 均依赖于 commons-validator-1.7 版本,而项目中如果有类似于 aliyun-log-appender 老版本的依赖,会传递依赖于旧版本的 commons-validator ,如下图所示:

Maven在遇到依赖冲突时,首先会采用就近原则,上图中第一个传递依赖有5个层级,而下一个传递依赖只有4个层级,所以就近取到了错误的1.4.0版本

其它的服务更多是单模块项目,直接依赖于RocketMQ的client代码,而出现问题的服务是一个多模块依赖关系的服务,最终依赖传递了3层,导致依赖链路变长,最终优先依赖了链路更近的由 log-appender 引入进来的 1.4.0 版本。

相关文章: Maven依赖冲突避坑指北