白话链路追踪基础技术

2020-12-13  本文已影响0人  大强斌


1 要解决的目标 及理论模型

   1.1 背景

           互联网电商行业,一次用户的购买,涉及背后的系统很多,如果出现问题,我应该怎样快速定位是那个系统环节出现问题?A系统,B系统,C系统,巨量的体系下面,每秒成千上万的流量,那都是钱A! 老板允许你交易失败一分钟?这就是链路追踪要解决得问题。如何解决?如何统计每个环节消耗的时间?最简单的就是从入口仍一个标识进去,每经过一个环节,建立子环节和整条链路的关系,并打印代码执行的时间,输出到日志中,最后通过数据汇总,分析,完成关系运算,输出到界面中,可以吗?ok?yes,对就那简单。这就是一个思路。elk实现方案,就是这样搞的?elk不懂得自己百度。还有没有其他方案?除了能监控关键方法层面,代码层面有没有?代码层面的监控,能提供直接可靠的 问题定位机制,能快速发现问题。有 metrics 技术,可以参考  https://www.jianshu.com/p/effe8e259d25,就不依依解释了。

1.2 我们要监测什么东西?

        技术体系的变更引起了监测对象的变化,不同的技术监测对象要求有不同的方案,apm技术也在发展,以前中间件的监控,就能满足体系要求,随着微服务技术发展,apm框架技术也在升级迭代。下面就从最原的基础理论谈起。

    1.2.1 

     业务跟踪监测原理

       我们如何知道一条链路经过的各个节点?这就是业务监控。那我门采用什么数据结构? 比如一次访问要经过 A,B,C 三个节点,他们之间可能是并行的,也可能是串行的。访问一个节点的时候,他的上级是什么?所以会引入parent的概念。跨线程,怎办?大家都应该有一个统一的上级,这就引入了tracementId的概念.我如何记录当前处理的节点数据,为了解决这个问题就引入了栈的概念。举例如下:访问经过的路径如下

        user -> jetty -> A(spring bean) -> B(spring bean)

                                -> C(spring bean)

        那应该怎处理呢?

    即线程按顺序调用到jetty,A,B,C,pinpoint会将其依次放入到CallStack中(执行trace.traceBlockBegin()),当方法执行完毕(调用trace.traceBlockEnd()),会从栈中弹出。

此时会产生几个SpanEvent,关于组织其调用关系属性有如下三个:

spanId:标志这个SpanEvent归属于哪个span

deepth:标志调用深度,即入栈时的当前数组下标

sequence:标志调用顺序

而对于例子中的几个SpanEvent其值时什么呢?

根据深度和顺序号,就很容易能够梳理出调用树了。

需要注意一点,上述SpanEvent B和C为什么深度一样?因为B和C是串行的两个方法,B调用完毕,会出栈,C入栈,所以C和B的深度是一样的。这就涉及到了本地线程的技术,存储上下下文变量。

跨系统怎办?简单啊,通过数据传输,那就把上个相关所属于的 segmentId,最后一次访问节点的相关信息传递到下一个进行,做为上级来处理呢。

1.2.2 除了业务监控,我们还需要监控什么?jvm监控,内存监控,垃圾回收监控,cpu监控,线程数量,这些都是对资源的监控。 这些可以通过java提供的ManagementFactory 技术实现。

1.2.3 除了这些要监控的对象,我们还要考虑什么问题?

监控数据 再业务系统中呈现爆炸性的增长,我们应该怎存储?增加监控系统我们对我门的业务系统性能带来什么冲击?我们的是否每条数据都需要?

这就涉及到采样频率的考虑,能不能根据性能自动调节采样率?elk就提供了自适应采用方案。

1.2.4  怎样实现的问题?

      说了那多上面的东西,用java技术我门应该怎实现?监控的对象,包过业务,redis ,数据源,各种中间件,我们应该用怎样的架构组织去实现,最大程度上的抽象,最小的代码的开发?这就是插件技术的引用。链路中间的通信,我们应该用什么协议实现?一方面是应用代码,一方面是监控代码,我们手写代码?侵入到业务逻辑中?NONO,我们一定要做成公共组件,这就是aop思想,那我门在什么时机对 代码进行更改?那多需要更改的方法,总不能一个个配置切入点把。这就需要利用 java angent 技术,从虚拟机层面对类进行改写,可以通过https://www.jianshu.com/p/63c328ca208d了解。本质上来说就是对java 字节码就行修改,这些都是字节码技术。因为涉及到底层,难啊!字节码技术各自不同的特点,能有我们程序员直接看懂,面向对象开发的吗?有!技术的进步就是为了提高生产力,让我门当傻瓜把。 

那多插件。我们应该怎样组织起来,方面集中管理,如果需要扩展插件,我们应该怎办?这就需要考虑扩展性的问题。这方面skywalking 做的就比pinpont 比较好,我只需要简单配置下我拦截的类,和 方法,和要插入的公共业务,通过配置就可以实现,其他的你别跟我谈 什么agent 技术,什么 字节码修改,你的体系api ,我通通不需要知道?怎实现的?

2 目前市面的技术体系

     我们一切从头开始制造轮子?你扯蛋呢?巨人也是站在巨人的肩膀上,才能看的更远。市面上有 Zipkin Pinpoint SkyWalking CAT Jaeger appdash 我们应该怎样选型? 下面就进行回答这个问题

3技术选型

    每个技术的演变都是有其背景和应用的领域.

                 Zipkin 是 Spring Cloud 指定的默认性能监控工具,

                   2 Jaeger 纳入云原生计算基金会(CNCF)的孵化项目 主要为k8s 服务的基于go语言的.

                   3 Pinpoint 是韩国搞的.    

                    4skywalking 是国人搞的,已经进入了Apache 基金会,目前国内比较火。国内强烈建议这个,由于 应用了byptebuddy字节码 技术,面向对象思维编程模型,性能也不错,最终导致傻瓜式开发。配置配置完事了。和pinpoint 相比不需要了解 底层api 开发效率和易用性都非常高。

                    5探针性能影响

                            比较关注探针的性能,毕竟APM定位还是工具,如果启用了链路监控组建后,直接导致吞吐量降低过半,那也是不能接受的。对skywalking、zipkin、pinpoint进行了压测,并与基线(未使用探针)的情况进行了对比。

                            选用了一个常见的基于Spring的应用程序,他包含Spring Boot, Spring MVC,redis客户端,mysql。 监控这个应用程序,每个trace,探针会抓取5个span(1 Tomcat, 1 SpringMVC, 2 Jedis, 1 Mysql)。这边基本和skywalkingtest的测试应用差不多。

                                模拟了三种并发用户:500,750,1000。使用jmeter测试,每个线程发送30个请求,设置思考时间为10ms。使用的采样率为1,即100%,这边与生产可能有差别。pinpoint默认的采样率为20,即50%,通过设置agent的配置文件改为100%。zipkin默认也是1。组合起来,一共有12种。下面看下汇总表:

                        从上表可以看出,在三种链路监控组件中,skywalking的探针对吞吐量的影响最小,zipkin的吞吐量居中。pinpoint的探针对吞吐量的影响较为明显,在500并发用户时,测试服务的吞吐量从1385降低到774,影响很大。然后再看下CPU和memory的影响,在内部服务器进行的压测,对CPU和memory的影响都差不多在10%之内。

                    6 collector的可扩展性

            skywalking 有集群模式,collector 向zookper 注册,进行扩展服务能力。

              zipkin 通过mq 方式进行消息订阅,进行服务扩展

              pinpoint 也有集群模式,通过zookper  提供 在agent 和collector 和collector 和hbase 之间的高可用 

          7 监控的细粒度 pinpoint>skywalking>zipkin

以上都支持微服务和云生态。下面基于实现原理,支持的语言,支持的组件支持的数据存储,扩展性,多维角度去解析,架构基本一样,实现细节侧重点不同,选型是兼顾目前公司的技术和长远发展,取舍后的结果。

8 collector的可扩展性

pinpoint 和skywalking 通过java agent 提供的接口 利用字节码增强技术实现无侵入性。在虚拟机加载类的时候,动态增强类功能。 

9 监控力度

4通过刨析 pin个技术框架,查看最基本的模型构成

1 整体业务流程

    pinpoint 

            监控的业务有: 业务跟踪,通过插件实现。

            业务机器监控:监控业务机器的 ip 端口,进程号,状态,jvm 

            agent 状态 监控:收集各种agent相关的信息,比如cpu,jvm内存,trace响应时间等数据

            支持的协议:Tcp ,Udp,Grpc,thrift 协议

整体架构处理

collect端的主要框架类图

客户端体系整合涉及的知识点

     1 系统加载插件 步骤

                Pinpoint Agent 随 JVM 一起启动

                Agent 加载所有 plugin 目录下的插件

                Agent 调用已加载的插件的                                              ProfilerPlugin.setup(ProfilerPluginSetupContext) 方法

 2 插件开发层,实现的功能如下

            声明在哪个拦截点 进行拦截 ,TransformerCallback 声明哪些类需要被增强,和如何增强。举例如下:

asm 知识详情见

https://developer.ibm.com/zh/articles/j-lo-asm30/

3 插件业务逻辑层次

4存储和发送层

模块的管理采用guice 技术,实现了依赖注入,根据配置灵活配置实现类和依赖类

参考 https://blog.csdn.net/xxxlxy2008/article/details/89736544

比如

5

每当类被加载的时候,Pinpoint Agent 会寻找注册到该类的回调 TransformerCallback

6

    如果 TransformerCallback 被注册,Agent 就调用它的 doInTransform 方法

            TransformerCallback 修改目标类的字节码 (例如添加拦截器、字段等)

            修改后的代码返回到 JVM,类被加载的时候使用修改后的字节码                    TransformerCallback 修改目标类的字节码 (例如添加拦截器、字段等

             修改后的代码返回到 JVM,类被加载的时候使用修改后的字节码

skywalking 

    思路 和pinpoint 类似,只是具体细节不一样。

架构 

    1加载插件

入口

 2插件里面定义了,那些类,哪些方法,执行增强。

  3 agent启动得时候,每加载一个类,查找类有没有匹配得增强。

            通过 agentBuilder 对 transform 方法进行增强,

            每加载一个类,查找对应的插件

            最终调用插件的 define 方法进行增强

            如果有得话通过bytebuddy 执行增强。

collector 端处理,简要举例说明:

业务跟踪过程

存储层,采用了生产者和消费者模式,存储是多级分片集合,接收到数据可以放入不同的分片上,分片有对应的数据消费者,提高消费能力。后面继续 讲解。

上一篇下一篇

猜你喜欢

热点阅读