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

本文介绍在不直接修改Jedis源码的情况下,通过JavaAssist实现自动化监控埋点,解决因类加载导致的字节码修改失败问题,并针对不同Jedis版本方法定义差异提供解决方案。

由于监控链路升级的需要,需要对应用访问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定义,因此不存在该问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值