自学内容网 自学内容网

PDF书籍《手写调用链监控APM系统-Java版》第8章 插件与链路的结合:Gson插件实现

本人阅读了 Skywalking 的大部分核心代码,也了解了相关的文献,对此深有感悟,特此借助巨人的思想自己手动用JAVA语言实现了一个 “调用链监控APM” 系统。本书采用边讲解实现原理边编写代码的方式,看本书时一定要跟着敲代码。

作者已经将过程写成一部书籍,奈何没有钱发表,如果您知道渠道可以联系本人。一定重谢。

本书涉及到的核心技术与思想

JavaAgent , ByteBuddy,SPI服务,类加载器的命名空间,增强JDK类,kafka,插件思想,切面,链路栈等等。实际上远不止这么多,差不多贯通了整个java体系。

适用人群

自己公司要实现自己的调用链的;写架构的;深入java编程的;阅读Skywalking源码的;

版权

本书是作者呕心沥血亲自编写的代码,不经同意切勿拿出去商用,否则会追究其责任。

原版PDF+源码请见:

本章涉及到的工具类也在这里面:

PDF书籍《手写调用链监控APM系统-Java版》第1章 开篇介绍-CSDN博客

第8章 插件与链路的结合:Gson插件实现

Gson是一个json解析工具,也没跨线程或进程调用,非常适合LocalSpan案例。

Gson在将json字符串反序列化成对象时,执行的类和方法信息如下:

类名:com.google.gson.Gson

方法:fromJson(第一个参数为JsonReader.class)

非JDK类库

Gson在将对象序列化成json字符串时,执行的类和方法信息如下:

类名:com.google.gson.Gson

方法:toJson (第二个参数为JsonWriter.class)

非JDK类库

在插件apm-agent-plugins 聚合模块下新建一个gson-plugin插件项目。POM文件添加gosn依赖,内容如下:

<dependencies>
    <dependency>
        <groupId>com.hadluo.apm</groupId>
        <artifactId>apm-commons</artifactId>
        <version>1.0</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.8.5</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

新增插件定义类GsonFromInstrumentation:

public class GsonFromInstrumentation extends AbstractClassEnhancePluginDefine {
    @Override
    public String enhanceClass() {
        // 拦截的类
        return "com.google.gson.Gson";
    }
    @Override
    public MethodsInterceptPoint[] configMethodsInterceptPoint() {
        return new MethodsInterceptPoint[]{
                new MethodsInterceptPoint() {
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        // 拦截fromJson方法,且第一个参数为JsonReader类型
                        return ElementMatchers.named("fromJson").and(ElementMatchers.takesArgument(0, JsonReader.class));
                    }
                    @Override
                    public String getMethodsInterceptor() {
                        // 拦截逻辑执行的拦截器
                        return "com.hadluo.apm.plugin.gson.GsonFromInterceptor";
                    }
                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                }
        };
    }
}

同理新增GsonToInstrumentation。

新增插件定义配置文件hadluo-apm-plugin.def,内容:

gson-from=com.hadluo.apm.plugin.gson.GsonFromInstrumentation
gson-to=com.hadluo.apm.plugin.gson.GsonToInstrumentation

新增拦截逻辑执行的拦截器GsonFromInterceptor :

public class GsonFromInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes) throws Throwable {
        // 创建 local span
        TraceContextManager manager = ServiceManager.INSTANCE.getService(TraceContextManager.class);
        AbstractSpan localSpan = manager.createLocalSpan("Gson/FromJson");
        localSpan.setComponent("GSON");
        int length = allArguments[0].toString().length();
        localSpan.setTag("length" , Integer.toString(length)) ;
    }
    @Override
    public Object afterMethod(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
        TraceContextManager manager = ServiceManager.INSTANCE.getService(TraceContextManager.class);
        manager.stopSpan();
        return ret;
    }
    @Override
    public void handleMethodException(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
        TraceContextManager manager = ServiceManager.INSTANCE.getService(TraceContextManager.class);
        manager.activeSpan().log(t) ;
    }
}

GsonFromInterceptor 在beforeMethod中创建了LocalSpan,tag信息你可以自己定义,我这里就随便取了长度。在异常时采集log到span里面,在afterMethod中stopSpan。

同理我们可以编写出GsonToInterceptor,代码如下:

public class GsonToInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public void beforeMethod(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes) throws Throwable {
        // 创建 local span
        TraceContextManager manager = ServiceManager.INSTANCE.getService(TraceContextManager.class);
        AbstractSpan localSpan = manager.createLocalSpan("Gson/ToJson");
        localSpan.setComponent("GSON");
        int length = allArguments[0].toString().length();
        localSpan.setTag("length" , Integer.toString(length)) ;
    }
    @Override
    public Object afterMethod(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
        TraceContextManager manager = ServiceManager.INSTANCE.getService(TraceContextManager.class);
        manager.stopSpan();
        return ret;
    }
    @Override
    public void handleMethodException(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
        TraceContextManager manager = ServiceManager.INSTANCE.getService(TraceContextManager.class);
        manager.activeSpan().log(t) ;
    }
}

到此gson插件代码编写完成,根据四部曲完善其它配置,我就不讲解了。

修改测试controller代码,在接口中进行json操作:

@GetMapping("/order")
public String order(@RequestParam("shopId")String shopId) throws ClassNotFoundException {
    System.out.println("下单请求 商品ID:" + shopId);
    Gson gson = new Gson();
    String json = gson.toJson(shopId);
    gson.fromJson(json, String.class);
    return UUID.randomUUID().toString();
}

打包测试,结果

{
    "msgTypeClass": "com.hadluo.apm.commons.kafka.Segment",
    "sampleTime": 1733369232204,
    "serviceName": null,
    "serviceInstance": "bc640bee591447c2869b1c66ef2907be@192.168.2.233",
    "traceId": "495c2b8bbf49424baa796b43726d256a.44.17333692321780001",
    "traceSegmentId": "495c2b8bbf49424baa796b43726d256a.44.17333692321780000",
    "spans": [
        {
            "spanId": 1,
            "parentSpanId": 0,
            "startTime": 0,
            "endTime": 1733369232195,
            "refs": [

            ],
            "operationName": "Gson/ToJson",
            "peer": null,
            "spanType": "Local",
            "spanLayer": null,
            "component": "GSON",
            "tags": {
                "length": "2"
            },
            "logs": {

            }
        },
        {
            "spanId": 2,
            "parentSpanId": 0,
            "startTime": 0,
            "endTime": 1733369232195,
            "refs": [

            ],
            "operationName": "Gson/FromJson",
            "peer": null,
            "spanType": "Local",
            "spanLayer": null,
            "component": "GSON",
            "tags": {

            },
            "logs": {

            }
        },
        {
            "spanId": 0,
            "parentSpanId": -1,
            "startTime": 1733369232179,
            "endTime": 1733369232204,
            "refs": [

            ],
            "operationName": "/order",
            "peer": null,
            "spanType": "Entry",
            "spanLayer": "HTTP",
            "component": "Tomcat",
            "tags": {
                "http.method": "GET",
                "url": "/order"
            },
            "logs": {

            }
        }
    ]
}

结果分析:

由于tomcat插件还存在,所以第一个span为EntrySpan,然后是toJson的LocalSpan,最后是fromJson的LocalSpan 。 分析上述json数据也是如此。


原文地址:https://blog.csdn.net/LuoZheng4698729/article/details/144735289

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!