Skywalking之四原理

个人专题目录


Skywalking原理

1. java agent原理

要使用Skywalking去监控服务,需要在其 VM 参数中添加 “-
javaagent:/usr/local/skywalking/apache-skywalking-apm-bin/agent/skywalking-agent.jar"。这里就
使用到了java agent技术。

Java agent 是什么?

Java agent是java命令的一个参数。参数 javaagent 可以用于指定一个 jar 包。

  1. 这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。

  2. Premain-Class 指定的那个类必须实现 premain() 方法。

当Java 虚拟机启动时,在执行main函数之前,JVM会先运行-javaagent所指定jar包内Premain-Class这个类的premain方法 。

如何使用java agent?

使用 java agent 需要几个步骤:

  1. 定义一个 MANIFEST.MF 文件,必须包含 Premain-Class 选项,通常也会加入Can-Redefine-
    Classes 和 Can-Retransform-Classes 选项。
  2. 创建一个Premain-Class 指定的类,类中包含 premain 方法,方法逻辑由用户自己确定。
  3. 将 premain 的类和 MANIFEST.MF 文件打成 jar 包。
  4. 使用参数 -javaagent: jar包路径 启动要代理的方法。

1.1 搭建java agent工程

使用maven创建java_agent_demo工程:

新建PreMainAgent类:

public class PreMainAgent {

/**
* 在这个 premain 函数中,开发者可以进行对类的各种操作。
* 1、agentArgs 是 premain 函数得到的程序参数,随同 “– javaagent”一起传入。与 main
函数不同的是,
* 这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。
* 2、Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。*
* java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这
个包的核心部分,
* 集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
类中提供两个静态方法,方法名均为premain,不能拼错。
在pom文件中添加打包插件:
* @param agentArgs
* @param inst
*/
   public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("=========premain方法执行1========");
        System.out.println(agentArgs);
    }
/**
* 如果不存在 premain(String agentArgs, Instrumentation inst)
* 则会执行 premain(String agentArgs)
* @param agentArgs
*/
    public static void premain(String agentArgs) {
        System.out.println("=========premain方法执行2========");
        System.out.println(agentArgs);
    }
}

类中提供两个静态方法,方法名均为premain,不能拼错。

在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.xubh</groupId>
    <artifactId>java-agent-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.9.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <!--自动添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <Premain-Class>PreMainAgent</Premain-Class>
                            <Agent-Class>PreMainAgent</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

该插件会在自动生成META-INF/MANIFEST.MF文件时,帮我们添加agent相关的配置信息。

使用maven的package命令进行打包:打包成功之后,复制打包出来的jar包地址。

1.2 搭建主工程

使用maven创建java_agent_user工程:

Main类代码:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

先运行一次,然后点击编辑MAIN启动类:

在VM options中添加代码:
代码

-javaagent:路径\java-agent-demo-1.0-SNAPSHOT.jar=HELLOAGENT

启动时加载javaagent,指向1.1中编译出来的java agent工程jar包地址,同时在最后追加参数
HELLOAGENT。

运行MAIN方法,查看结果:

可以看到java agent的代码优先于MAIN函数的方法运行,证明java agent运行正常。

1.3 统计方法调用时间

Skywalking中对每个调用的时长都进行了统计,使用ByteBuddy和Java agent技术来统计方法的调用时长。

Byte Buddy是开源的、基于Apache 2.0许可证的库,它致力于解决字节码操作和instrumentation API
的复杂性。Byte Buddy所声称的目标是将显式的字节码操作隐藏在一个类型安全的领域特定语言背
后。通过使用Byte Buddy,任何熟悉Java编程语言的人都有望非常容易地进行字节码操作。Byte
Buddy提供了额外的API来生成Java agent,可以轻松的增强我们已有的代码。

添加依赖:

<dependencies>
    <dependency>
        <groupId>net.bytebuddy</groupId>
        <artifactId>byte-buddy</artifactId>
        <version>1.9.2</version>
    </dependency>
    <dependency>
        <groupId>net.bytebuddy</groupId>
        <artifactId>byte-buddy-agent</artifactId>
        <version>1.9.2</version>
    </dependency>
</dependencies>

修改PreMainAgent代码:

public class PreMainAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
    //创建一个转换器,转换器可以修改类的实现
    //ByteBuddy对java agent提供了转换器的实现,直接使用即可
    AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
        public DynamicType.Builder<?> transform(DynamicType.Builder<?>
builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule
javaModule) {
            return builder
                // 拦截任意方法
                .method(ElementMatchers.<MethodDescription>any())
                // 拦截到的方法委托给TimeInterceptor
                .intercept(MethodDelegation.to(MyInterceptor.class));
        }
    };
    new AgentBuilder // Byte Buddy专门有个AgentBuilder来处理Java Agent的场景
        .Default()
        // 根据包名前缀拦截类
        .type(ElementMatchers.nameStartsWith("com.agent"))
        // 拦截到的类由transformer处理
        .transform(transformer)
        .installOn(inst);
    }
}

先生成一个转换器,ByteBuddy提供了java agent专用的转换器。通过实现Transformer接口利用
builder对象来创建一个转换器。转换器可以配置拦截方法的格式,比如用名称,拦截所有方法,并定义一个拦截器类MyInterceptor。

创建完拦截器之后可以通过Byte Buddy的AgentBuilder建造者来构建一个agent对象。AgentBuilder可以对指定的包名前缀来生效,同时需要指定转换器对象。

MyInterceptor类:

public class MyInterceptor {
    @RuntimeType
    public static Object intercept(@Origin Method method,
                                   @SuperCall Callable<?> callable)
            throws Exception {
        long start = System.currentTimeMillis();
        try {
            //执行原方法
            return callable.call();
        } finally {
            //打印调用时长
            System.out.println(method.getName() + ":" + (System.currentTimeMillis() - start)  + "ms");
        }
    }
}

MyInterceptor就是一个拦截器的实现,统计的调用的时长。参数中的method是反射出的方法对象,而
callable就是调用对象,可以通过callable.call()方法来执行原方法。

重新打包,执行maven package命令。接下来修改主工程代码。主工程将Main类放置到com.agent 包下。修改代码内容为:

public class Main {
    public static void main(String[] args) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello World");
    }
}

休眠1秒,使统计时长的演示效果更好一些。执行main方法之后显示结果:

在没有修改代码的情况下,利用java agent和Byte Buddy统计出了方法的时长,Skywalking的agent也是基于这些技术来实现统计调用时长。

2. Open Tracing介绍

OpenTracing通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。OpenTracing中最核心的概念就是
Trace。

2.1 Trace的概念

在广义上,一个trace代表了一个事务或者流程在(分布式)系统中的执行过程。在OpenTracing标准
中,trace是多个span组成的一个有向无环图(DAG),每一个span代表trace中被命名并计时的连续性的执行片段。

image-20200225112448574.png

客户端发起的一次请求,就可以认为是一个Trace。将上面的图通过Open Tracing的语义修改完之
后做可视化,得到下面的图:

image-20200225112756931.png

图中每一个色块其实就是一个span。

2.2 Span的概念

一个Span代表系统中具有开始时间和执行时长的逻辑运行单元。span之间通过嵌套或者顺序排列建立逻辑因果关系。

Span里面的信息包括:操作的名字,开始时间和结束时间,可以附带多个 key:value 构成的 Tags(key
必须是String,value可以是 String, bool 或者数字),还可以附带 Logs 信息(不一定所有的实现都支持)
也是 key:value形式。

下面例子是一个 Trace,里面有8个 Span:

                [Span A] ←←←(the root span)
                   |
            +------+------+
            |             |
        [Span B]       [Span C] ←←←(Span C 是 Span A 的孩子节点, ChildOf)
            |             |
        [Span D]      +---+-------+
                      |           |
                   [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                               ↑
                                               ↑
                                               ↑
                                (Span G 在 Span F 后被调用, FollowsFrom)

一个span可以和一个或者多个span间存在因果关系。OpenTracing定义了两种关系: ChildOf
FollowsFrom这两种引用类型代表了子节点和父节点间的直接因果关系。未来,OpenTracing将支
持非因果关系的span引用关系。(例如:多个span被批量处理,span在同一个队列中,等等)

ChildOf 很好理解,就是父亲 Span 依赖另一个孩子 Span。比如函数调用,被调者是调用者的孩子,比
如说 RPC 调用,服务端那边的Span,就是 ChildOf 客户端的。很多并发的调用,然后将结果聚合起来
的操作,就构成了 ChildOf 关系。

如果父亲 Span 并不依赖于孩子 Span 的返回结果,这时可以说它他构成 FollowsFrom 关系。

2.3 Log的概念

每个span可以进行多次Logs操作,每一次Logs操作,都需要一个带时间戳的时间名称,以及可选的任意大小的存储结构。

如下图是一个异常的Log:

image-20200225113608526.png

2.4 Tags的概念

每个span可以有多个键值对(key:value)形式的Tags,Tags是没有时间戳的,支持简单的对span进行注解和补充。

如下图就是一个Tags的详细信息,其中记录了数据库访问的SQL语句等内容。

image-20200225113736293.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容