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;
    }

结果如下

在这里插入图片描述

标红的节点就是我自定义的插件,后面所有链路也成功连接了

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐