美文网首页
skywalking(2)自定义插件【okhttp插件-监控co

skywalking(2)自定义插件【okhttp插件-监控co

作者: 小胖学编程 | 来源:发表于2021-07-10 21:05 被阅读0次

    使用skywalking时经常会出现断层,则是因为skywalking并没有提供对应的插件导致无法在链路图中体现调用流程。

    1. 起因

    1.1 腾讯云sdk版本

            <!--腾讯云cos最新的api文档-->
            <dependency>
                <groupId>com.tencentcloudapi</groupId>
                <artifactId>tencentcloud-sdk-java</artifactId>
                <!-- go to https://search.maven.org/search?q=tencentcloud-sdk-java and get the latest version. -->
                <!-- 请到https://search.maven.org/search?q=tencentcloud-sdk-java查询所有版本,最新版本如下 -->
                <version>3.1.271</version>
            </dependency>
    

    代码位置:

    image.png

    1.2 skywalking的配置

    使用腾讯云cos文件存储的sdk时,skywalking并未将链路打印出来,从而出现了断层。

    image.png

    在skywalking的github上查询,并未发现okHttp的插件。

    image.png

    由上图可知,只有okhttp3的插件。这就是为什么okHttp3的调用链路可以在skywalking上显示,而腾讯云sdk的调用会出现断层。

    2. 解决版本

    代码按照okHttp3插件进行仿写,该项目作为多个plugins插件的编写项目,采用多模块方式。

    结构图:

    image.png

    2.1 父pom依赖

    这些依赖必须存在,可以根据项目实际的skywalking版本设置skywalking.version版本号。

    代码详见—附录1

    2.2 okhttp插件编写

    2.2.1 配置文件

    skywalking首先去skywalking-plugin.def配置中定位将要增强的类。

    okhttp=com.tellme.define.CallInstrumentation
    

    key:不限定,可自由定义
    value:ClassInstanceMethodsEnhancePluginDefine的实现类

    2.2.2 ClassInstanceMethodsEnhancePluginDefine实现类

    声明增强的类,以及将要拦截的方法。

    import net.bytebuddy.description.method.MethodDescription;
    import net.bytebuddy.matcher.ElementMatcher;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
    import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
    import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
    
    import static net.bytebuddy.matcher.ElementMatchers.any;
    import static net.bytebuddy.matcher.ElementMatchers.named;
    
    /**
     * okHttp插件入口
     *
     */
    public class CallInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    
        /**
         * 将要增强的类
         */
        private static final String ENHANCE_CLASS = "com.squareup.okhttp.Call";
    
        /**
         * 增强代码的拦截器
         */
        private static final String INTERCEPT_CLASS = "com.xdf.pscommon.CallInterceptor";
    
    
        /**
         * 声明需要增强的类
         */
        @Override
        protected ClassMatch enhanceClass() {
            return NameMatch.byName(ENHANCE_CLASS);
        }
    
        /**
         * 要拦截的构造方法
         */
        @Override
        public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
            return new ConstructorInterceptPoint[]{
                    new ConstructorInterceptPoint() {
                        @Override
                        public ElementMatcher<MethodDescription> getConstructorMatcher() {
                            return any();
                        }
    
                        @Override
                        public String getConstructorInterceptor() {
                            return INTERCEPT_CLASS;
                        }
                    }
            };
        }
    
        /**
         * 要拦截的普通方法
         */
        @Override
        public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
            return new InstanceMethodsInterceptPoint[]{
                    new InstanceMethodsInterceptPoint() {
                        @Override
                        public ElementMatcher<MethodDescription> getMethodsMatcher() {
                            return named("execute");
                        }
    
                        @Override
                        public String getMethodsInterceptor() {
                            return INTERCEPT_CLASS;
                        }
    
                        @Override
                        public boolean isOverrideArgs() {
                            return false;
                        }
                    }
            };
        }
    
    }
    

    2.2.3 增强方法的拦截器

    本质上采用的是AOP对方法进行增强,并将Span的数据设置到header中传递到别的服务。

    import com.squareup.okhttp.Headers;
    import com.squareup.okhttp.Request;
    import com.squareup.okhttp.Response;
    import org.apache.skywalking.apm.agent.core.context.CarrierItem;
    import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
    import org.apache.skywalking.apm.agent.core.context.ContextManager;
    import org.apache.skywalking.apm.agent.core.context.tag.Tags;
    import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
    import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
    import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.net.URL;
    
    /**
     * 增强方法的拦截器
     */
    public class CallInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor {
    
        /**
         * 拦截构造方法,将第二个参数request对象传递下去
         */
        @Override
        public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable {
            //设置第一个参数
            objInst.setSkyWalkingDynamicField(allArguments[1]);
        }
    
        /**
         * 前置通知
         */
        @Override
        public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
            Request request = (Request) objInst.getSkyWalkingDynamicField();
    
            ContextCarrier contextCarrier = new ContextCarrier();
    
    
            URL requestUrl = request.url();
    
            AbstractSpan span = ContextManager.createExitSpan(requestUrl.toURI()
                    .getPath(), contextCarrier, requestUrl.getHost() + ":" + requestUrl
                    .getPort());
            span.setComponent(ComponentsDefine.OKHTTP);
            Tags.HTTP.METHOD.set(span, request.method());
            Tags.URL.set(span, requestUrl.toURI().toString());
            SpanLayer.asHttp(span);
    
    
            Field headersField = Request.class.getDeclaredField("headers");
            headersField.setAccessible(true);
    
            Headers.Builder headerBuilder = request.headers().newBuilder();
            CarrierItem next = contextCarrier.items();
            while (next.hasNext()) {
                next = next.next();
                headerBuilder.set(next.getHeadKey(), next.getHeadValue());
            }
            headersField.set(request, headerBuilder.build());
        }
    
        @Override
        public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
            Response response = (Response) ret;
            if (response != null) {
                int statusCode = response.code();
                AbstractSpan span = ContextManager.activeSpan();
                if (statusCode >= 400) {
                    span.errorOccurred();
                    Tags.STATUS_CODE.set(span, Integer.toString(statusCode));
                }
            }
    
            ContextManager.stopSpan();
    
            return ret;
        }
    
        @Override
        public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
            AbstractSpan abstractSpan = ContextManager.activeSpan();
            abstractSpan.log(t);
        }
    }
    

    2.2.4 编译打包

    编译打包放到服务器的apache-skywalking-apm-bin/agent/plugins下,重启应用服务器。即可完成自定义组件的链路追踪。

    image.png

    附录

    附录1—父pom依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.xdf.pscommon</groupId>
        <artifactId>skywalking-plugins</artifactId>
        <packaging>pom</packaging>
        <version>1.0.0-SNAPSHOT</version>
        <modules>
            <module>apm-okhttp-plugin</module>
        </modules>
    
        <properties>
            <skywalking.version>8.2.0</skywalking.version>
            <shade.package>org.apache.skywalking.apm.dependencies</shade.package>
            <shade.net.bytebuddy.source>net.bytebuddy</shade.net.bytebuddy.source>
            <shade.net.bytebuddy.target>${shade.package}.${shade.net.bytebuddy.source}</shade.net.bytebuddy.target>
        </properties>
    
    
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.22</version>
                <optional>true</optional>
            </dependency>
    
    
            <dependency>
                <groupId>org.apache.skywalking</groupId>
                <artifactId>apm-agent-core</artifactId>
                <version>${skywalking.version}</version>
                <scope>provided</scope>
            </dependency>
    
            <dependency>
                <groupId>org.apache.skywalking</groupId>
                <artifactId>apm-util</artifactId>
                <version>${skywalking.version}</version>
                <scope>provided</scope>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.10</version>
                <scope>provided</scope>
            </dependency>
    
        </dependencies>
        
        <build>
            <plugins>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <version>3.0.1</version>
                    <executions>
                        <execution>
                            <id>attach-sources</id>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
    
    
                <plugin>
                    <artifactId>maven-shade-plugin</artifactId>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <shadedArtifactAttached>false</shadedArtifactAttached>
                                <createDependencyReducedPom>true</createDependencyReducedPom>
                                <createSourcesJar>true</createSourcesJar>
                                <shadeSourcesContent>true</shadeSourcesContent>
                                <relocations>
                                    <relocation>
                                        <pattern>${shade.net.bytebuddy.source}</pattern>
                                        <shadedPattern>${shade.net.bytebuddy.target}</shadedPattern>
                                    </relocation>
                                </relocations>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>6</source>
                        <target>6</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    附录2—子pom依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>skywalking-plugins</artifactId>
            <groupId>com.xdf.pscommon</groupId>
            <version>1.0.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
        <packaging>jar</packaging>
        <artifactId>okhttp-plugin</artifactId>
    
    
        <dependencies>
            <dependency>
                <groupId>com.squareup.okhttp</groupId>
                <artifactId>okhttp</artifactId>
                <version>2.5.0</version>
            </dependency>
        </dependencies>
    
    </project>
    

    推荐阅读

    手把手教你编写Skywalking插件
    Skywalking 6.2.0中文文档

    相关文章

      网友评论

          本文标题:skywalking(2)自定义插件【okhttp插件-监控co

          本文链接:https://www.haomeiwen.com/subject/falfpltx.html