skywalking线程池插件,解决lambda使用问题
1. 简介
分布式链路追踪其实原理都很简单,每一个请求过来的时候,把一个context存到ThreadLocal里面,然后随着线程一路传递下去就行,一个请求大多数时候都是一个线程去执行下去所以并没有什么问题,但是如果在请求中使用了ThreadPool那么ThreadLocal中的context就会失联(如果使用的是Thread,ThreadLocal是可以继续传递下去的,具体原因可以去看ThreadLocal和ThreadPool的实现这里就不过多解释了),所以需要插件的支持去把这个失联的context给找回来
2. 官方插件
skywalking 的跨线程池 其实是已经有官方插件的支持了。实现的原理也是非常简单,他会去织入业务方自己实现的Runnable类,在执行run()方法之前去传递context。
- 使用方法:
修改skywalking-agent.jar所在目录的config/agent.config文件,在里面修改/添加配置
#他将会去把指定包下所有`Runnable`的实现类给织入,可以前缀匹配
jdkthreading.threading_class_prefixes=com.chy
然后在plugins文件夹里加入插件apm-jdk-threading-plugin.jar就能够使用了(如果是下载官方编译好的插件,apm-jdk-threading-plugin.jar被放在了bootstrap-plugins文件夹中,移动到plugins即可)
- 缺点:
虽然简单但是却有一个缺点,无法使用lambda表达式,比如下面的代码,就无法正常的把链路给连接起来
@GetMapping("/ajax/{setId}/async")
public List<DoctorListVO> getDoctorListasync(@PathVariable Integer setId) throws ExecutionException, InterruptedException {
//线程池访问
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<List<DoctorListVO>> submit = executor.submit(() -> {
//远程调用了别的服务
return doctorSetAdminService.queryDoctorList(setId);
});
Future<List<DoctorListVO>> submit2 = executor.submit(() -> {
return doctorSetAdminService.queryDoctorList(setId);
});
//和上面 queryDoctorList 一样只是queryDoctorListAsy() 方法都是打了 `@Async` 注解
doctorSetAdminService.queryDoctorListAsy(setId);
doctorSetAdminService.queryDoctorListAsy(setId);
List<DoctorListVO> doctorListVOS = submit.get();
List<DoctorListVO> doctorListVOS2 = submit2.get();
return doctorListVOS;
}
简单说明一下原因,上面提到过这个插件的原理是去修改所有Runnable的实现类,但是如果使用lambda去实现的Runnable类,虽然会生成一个匿名内部类,但是这个匿名内部类的和skywalking-agent使用的不同的类加载器,导致skywalking-agent无法去修改lambda表达式生成的Runnable的实现类.
3. 非官方插件
为了解决lambda的问题,博主自己写了一个插件去解决这个问题,思路也很简单,既然我不能去修改lambda生成的类,那么我就换个地方去修改他,比如在调用的时候再去修改。
所以我把切入点放在了ThreadPoolExecutor的execute(Runnable r)方法上面,也就是每当执行execute(Runnable r)方法的时候,我都会拦截下这个方法,然后把他的入参的Runnable给换成我的代理对象,然后继续去执行execute(Runnable r)方法
话不多说,直接上插件的地址,使用方法在readme里有写
这里需要注意的是,除了去把插件放入plugins文件夹外,还需要替换 skywalking-agent.jar 文件,原因在于博主的切入点是ThreadPoolExecutor 需要在字节码框架上做一些额外的配置。
还是执行这个代码
@GetMapping("/ajax/{setId}/async")
public List<DoctorListVO> getDoctorListasync(@PathVariable Integer setId) throws ExecutionException, InterruptedException {
//线程池访问
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<List<DoctorListVO>> submit = executor.submit(() -> {
//远程调用了别的服务
return doctorSetAdminService.queryDoctorList(setId);
});
Future<List<DoctorListVO>> submit2 = executor.submit(() -> {
return doctorSetAdminService.queryDoctorList(setId);
});
//和上面 queryDoctorList 一样只是queryDoctorListAsy() 方法都是打了 `@Async` 注解
doctorSetAdminService.queryDoctorListAsy(setId);
doctorSetAdminService.queryDoctorListAsy(setId);
List<DoctorListVO> doctorListVOS = submit.get();
List<DoctorListVO> doctorListVOS2 = submit2.get();
return doctorListVOS;
}
结果如下

标红的节点就是我自定义的插件,后面所有链路也成功连接了
更多推荐
所有评论(0)