JDNI加载资源导致的javassist修改字节码失败

由于监控链路升级的需要,需要对应用访问redis的性能进行监控,即需要在redis操作前后加上如下代码:

{
	java.util.Map<String, String> tags = new java.util.HashMap<>();
	tags.put("ops", "scan");
	tags.put("appName", "xxx");
	long begin = System.currentTimeMillis();
	try { 
		String result = get("key");
		return result;
	} catch (Exception e) {
		//record exception metric
		throw new RuntimeException(e);
	} finally {
		//record count metric
		//record time metric
	}}

手动修改jedis源码并重新deploy效率太低,因此采用javassist修改源码的方式完成自动化埋点。

基本思路如下:

1.获取所有jedis & pipeline实现的接口中所定义的方法

2.获取所有jedis & pipeline自身定义的新方法

3.修改1 & 2中获取的方法的body,进行监控埋点

使用上述方法在demo中修改时能够完成目标,但将jar包引入生产项目启动时,发现如下异常:

javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of  org/apache/catalina/loader/WebappClassLoader): attempted  duplicate class definition for name: "redis/clients/jedis/Jedis"
        at javassist.ClassPool.toClass(ClassPool.java:1099)
        at javassist.CtClass.toClass(CtClass.java:1265)

错误的原因在于一个类一旦被加载,就无法再修改字节码的内容了,这是JVM运行机制所决定的。

检查项目代码发现,该项目将redis封装为一个jndi数据源,spring容器使用JndiObjectFactoryBean初始化config并生成对应的client。在此过程中jedis类已经被加载,从而导致后续修改字节码失败。

为了解决该问题,我们尝试在ObjectFactory的实现类(RedisConfigFactory)中使用static代码块提前修改jedis字节码,成功启动并输出监控指标。

另附小坑一个:

2.9.0版本的Jedis中不是所有方法都由接口类定义,如以下方法:

public String set(final String key, final String value, final String nxxx, final String expx,
      final int time)

不是实现接口类方法而是在类中直接定义的,因此查找类方法时容易忽略。

在3.2.0版本中,set方法的众多参数已经由SetParam定义,因此不存在该问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值