• 调用链监控:Dapper、Istio... etc.


    目录

    Dapper,大规模分布式系统的跟踪系统by bigbully

    2.5.1 带外数据跟踪收集

    6.2 解决延迟的长尾效应

    7. 其他收获

     

    RPC服务追踪的原理与实践- SegmentFault 思否

    1. TraceID如何生成?

    2. 数据埋点如何做到对业务开发透明?如何在当前上下文中记录信息

    3. 怎么实现Trace信息在调用链上的传递?

    总结:

     

    业务链路监控(Google Dapper)和ThreadLocal - 简书

     

     

     

     

    Istio调用链埋点原理剖析—是否真的“零修改”? - InfoQ

    Dapper-Google大规模分布式跟踪系统 笔记

    要解决的问题

    设计目标

    Dapper应用场景

     

    APM(应用性能管理)与Dapper原理介绍- 谙忆-陈浩翔- CSDN博客



    Dapper,大规模分布式系统的跟踪系统by bigbully

    https://bigbully.github.io/Dapper-translation/

    翻译得不错,应该是对原文的翻译~~

    2.5.1 带外数据跟踪收集

    tip1:带外数据:传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方。为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道。

    tip2:这里指的in-band策略是把跟踪数据随着调用链进行传送out-of-band是通过其他的链路进行跟踪数据的收集Dapper的写日志然后进行日志采集的方式就属于out-of-band策略

    Dapper系统请求树树自身进行跟踪记录和收集带外数据。这样做是为两个不相关的原因。首先,带内收集方案--这里跟踪数据会以RPC响应头的形式被返回--会影响应用程序网络动态。在Google里的许多规模较大的系统中,一次跟踪成千上万的span并不少见。然而,RPC回应大小--甚至是接近大型分布式的跟踪的根节点的这种情况下-- 仍然是比较小的:通常小于10K。在这种情况下,带内Dapper的跟踪数据会让应用程序数据和倾向于使用后续分析结果的数据量相形见绌。其次,带内收集方案假定所有的RPC是完美嵌套的。我们发现,在所有的后端的系统返回的最终结果之前,有许多中间件会把结果返回给他们的调用者。带内收集系统是无法解释这种非嵌套的分布式执行模式的。

    >>> 上面一段有点没太来及看懂

    6.2 解决延迟的长尾效应

    考虑到移动部件的数量、代码库的规模、部署的范围,调试一个像全文搜索那样服务(第1节里提到过)是非常具有挑战性的。在这节,我们描述了我们在减轻全文搜索的延迟分布的长尾效应上做的各种努力。Dapper能够验证端到端的延迟的假设,更具体地说,Dapper能够验证对于搜索请求的关键路径。当一个系统不仅涉及数个子系统,而是几十个开发团队的涉及到的系统的情况下,端到端性能较差的根本原因到底在哪,这个问题即使是我们最好的和最有经验的工程师也无法正确回答。在这种情况下,Dapper可以提供急需的数据,而且可以对许多重要的性能问题得出结论。

    图7:全局搜索的跟踪片段,在不常遇到高网络延迟的情况下,在沿着关键路径的端到端的请求延迟,如图所示。

    在调试延迟长尾效应的过程中,工程师可以建立一个小型库,这个小型库可以根据DAPI跟踪对象来推断关键路径的层级结构。这些关键路径的结构可以被用来诊断问题,并且为全文搜索提供可优先处理的预期的性能改进。Dapper的这项工作导致了下列发现:

    • 在关键路径上的短暂的网络性能退化不影响系统的吞吐量但它可能会对延迟异常值产生极大的影响。在图7中可以看出,大部分的全局搜索的缓慢的跟踪都来源于关键路径的网络性能退化。>>>??这句是啥意思??
    • 许多问题和代价很高的查询模式来源于一些意想不到的服务之间的交互。一旦发现,往往容易纠正它们,但是Dapper出现之前想找出这些问题是相当困难的。
    • 通用的查询从Dapper之外的安全日志仓库中收取,并使用Dapper唯一的跟踪ID,与Dapper的仓库做关联。然后,该映射用来建立关于在全局搜索中的每一个独立子系统都很慢的实例查询的列表。

    7. 其他收获

    虽然迄今为止,我们在Dapper上的经验已经大致符合我们的预期,但是也出现了一些积极的方面是我们没有充分预料到的。首先,我们获得了超出预期的Dapper使用用例的数量,对此我们可谓欢心鼓舞。另外,在除了几个的在第6节使用经验中提到过的一些用例之外,还包括资源核算系统,对指定的通讯模式敏感的服务的检查工具,以及一种对RPC压缩策略的分析器,等等。我们认为这些意想不到的用例一定程度上是由于我们向开发者以一种简单的编程接口的方式开放了跟踪数据存储的缘故,这使得我们能够充分利用这个大的多的社区的创造力。除此之外,Dapper对旧的负载的支持也比预期的要简单,只需要在程序中引入一个用新版本的重新编译过的公共组件库(包含常规的线程使用,控制流和RPC框架)即可。

    Dapper在Google内部的广泛使用还为我们在Dapper的局限性上提供了宝贵的反馈意见。下面我们将介绍一些我们已知的最重要的Dapper的不足:

    • 合并的影响:我们的模型隐含的前提是不同的子系统在处理的都是来自同一个被跟踪的请求。在某些情况下,缓冲一部分请求,然后一次性操作一个请求集会更加有效。(比如,磁盘上的一次合并写入操作)。在这种情况下,一个被跟踪的请求可以看似是一个大型工作单元。此外,当有多个追踪请求被收集在一起,他们当中只有一个会用来生成那个唯一的跟踪ID,用来给其他span使用,所以就无法跟踪下去了。我们正在考虑的解决方案,希望在可以识别这种情况的前提下,用尽可能少的记录来解决这个问题。
    • 跟踪批处理负载:Dapper的设计,主要是针对在线服务系统,最初的目标是了解一个用户请求产生的系统行为。然而,离线的密集型负载,例如符合MapReduce[10]模型的情况,也可以受益于性能挖潜。在这种情况下,我们需要把跟踪ID与一些其他的有意义的工作单元做关联,诸如输入数据中的键值(或键值的范围),或是一个MapReduce shard。
    • 寻找根源:Dapper可以有效地确定系统中的哪一部分致使系统整个速度变慢,但并不总是能够找出问题的根源。例如,一个请求很慢有可能不是因为它自己的行为,而是由于队列中其他排在它前面的(queued ahead of)请求还没处理完。程序可以使用应用级的annotation把队列的大小或过载情况写入跟踪系统。此外,如果这种情况屡见不鲜,那么在ProfileMe[11]中提到的成对的采样技术可以解决这个问题。它由两个时间重叠的采样率组成,并观察它们在整个系统中的相对延迟。>>> 可以搞可以搞 >>> 
    • 记录内核级的信息:一些内核可见的事件的详细信息有时对确定问题根源是很有用的。我们有一些工具,能够跟踪或以其他方式描述内核的执行,但是,想用通用的或是不那么突兀的方式,是很难把这些信息到捆绑到用户级别的跟踪上下文中。我们正在研究一种妥协的解决方案,我们在用户层面上把一些内核级的活动参数做快照,然后绑定他们到一个活动的span上。

     

    RPC服务追踪的原理与实践- SegmentFault 思否

    https://segmentfault.com/p/1210000008713567/read

    讲的细节,不错

    1. TraceID如何生成?

    由于要唯一标示每一次调用,所以TraceID需要保证全局唯一。唯一的ID,第一个想到的当然是使用UUID,UUID是一个较为高效又使用方便的唯一ID生成方式,但问题是,zipkin要求TraceID是int64类型,不能是字符串,同时,UUID还有一个问题是不能保证单调有序。对此,有两个架构方案可选:
    - 使用数据库自增长ID来生成,同时需要解决以下问题:

    • 性能上,如果每次请求都访问数据库一次,会带来较大的性能损失,所以需要在客户端缓存一个区间的数字,当这个区间的数字不够时再从数据库获取。
    • 出于安全要求不能跟数据库直连的客户端不适合,如Web服务器。可以考虑使用一个中间服务作为ID分发。

    - 采用分机器(进程)的方式,保证机器间(进程间)ID不冲突,同时保证单机器(进程)内ID是递增不重复的。这种方式的最大好处时不需要中心化的节点进行ID分发,省掉了系统间的依赖。对于此种方案,最常用的算法是Twitter-Snowflake算法,也是我们最终选择的算法。Snowflake核心思想是将int64的除第一位外的其他63位分成三段,前面41位为时间戳、后面10位为工作机器(进程)ID,也称为WorkerID ,最后12位为递增序列号。
    (img)

    2. 数据埋点如何做到对业务开发透明?如何在当前上下文中记录信息

    所谓数据埋点,即将跟踪信息(TraceID、Span信息等)写入服务调用的上下文中,如果这个交给业务代码来完成的话,会导致业务代码变得冗余,同时如果业务代码忘记埋点,那就会丢失跟踪信息,所以在底层框架提供数据埋点,非常有必要。
    数据埋点主要包括四个阶段:

    • Client Send:客户端发起请求时,如果当前线程上下文已经有Trace信息,继续透传当前Trace信息,如果没有,表示一个信息的请求,生成信息的Trace信息进行传递。
    • Server Recieve: 服务端接收到请求时间点,此时从当前请求里获取Trace信息,并将当前信息存入线程上下文。
    • Server Send:服务端处理业务完成,准备返回响应时,标记业务处理完成,同时将当前Trace信息提交归档。
    • Client Receive:客户端接收到服务端响应时,标记服务调用完成,同时将当前Trace信息提交归档。

    如下的流程示例图说明各阶段埋点的位置,其中CS、SR为发起创建Trace信息到当前线程上下文的位置,CR、SS为归档提交Trace信息的位置

    以上解释了在什么地方埋点和收集Trace信息,但是如何将当前上下文中的信息进行临时存储,并保证线程安全呢?这一点可以借助ThreadLocal来完成,发起创建Trace信息时,往ThreadLocal中写入记录,当前请求过程中再发起新的请求时,从ThreadLocal中获取Trace信息继续往下传递,等信息可以提交归档的时候,从ThreadLocal读取,并清除ThreadLocal中的信息。但是有一个问题需要注意,当发起异步请求时,发起请求的线程和最终被服务响应锁唤起的线程不是同一个线程,对于这种情况,如果响应线程是可由当前线程创建,使用可继承InheritableThreadLocal即可,如果不是,如由线程池来创建,则需要实现特别的线程池管理。另外还有一个办法就是,如果异步回调代码是可以注入的,那我们就可以在发起响应回调的时候注入代码即可。我们的RPC Client里就是采取这种方式。

    3. 怎么实现Trace信息在调用链上的传递?

    前文说到,服务追踪的核心是将Trace信息(TraceID、SpanID)在整个调用链上进行传递,而这些类似上下文的信息,一般不适合作为参数置于服务调用方法里进行传递。如果是HTTP调用,我们可以用HTTP Header来传递信息是非常方便的,但是Thrift服务并没有所谓的Header信息可以传递。
    通过研究Thrift代码,发现在Thrift的传输协议实现里服务端读取数据反序列化协议的入口方法是:
    public abstract TMessage readMessageBegin() throws TException;
    返回的TMessage对象中,有一个name的属性,其存储的是需要调用的服务方法名,比如我们调用:UserService.getUser(1),那这里的name属性值就是“getUser”。既然这里name可以传递一个公用的字符串,那我们自然可以在此进行扩展,在name属性上传递更多信息。将name按一个文本格式协议,组装成一个header信息进行传递。

    //读取消息头
    TMessage message = iprot.readMessageBegin();
    // 提取Header文本
    int index = message.name.lastIndexOf(TMultiplexedProtocol.SEPARATOR);
    String headersValue = message.name.substring(0, index);
    //采取Http Header文本格式传递
    Headers headers = Headers.parseHeaders(headersValue);
    String traceID = headers.get(Constants.TPROTOCOL_HEADER_TRACE_ID);//arr[1];
    String spanID = headers.get(Constants.TPROTOCOL_HEADER_SPAN_ID);//arr[2];
    ....
    //将message.name还原,继续走thrift标准处理流程
    int len = headersValue.length() + TMultiplexedProtocol.SEPARATOR.length();
    String methodName = message.name.substring(len);
    TMessage standardMessage = new TMessage(
     methodName,
     message.type,
     message.seqid
     );
    actualProcessor.process(new SomeProtocol(standardMessage))

    关于此种扩展方式,Thrift标准库为了实现同一个server里host多个服务Processor,也采取了这种方式,只是标准库只扩展了一个ServiceName字段进行传递而已。具体可以参考Thrift标准库的TMultiplexedProtocol 与TMultiplexedProcessor的代码。

    总结:

    本文介绍了从原理到实现上介绍了如何实现RPC服务追踪的细节,其关键基于调用链的概念。但是在实现上为了做到业务开发透明,还要不影响业务性能,还是需要很多谨慎考虑的。同时,我们其实还有很多未尽事宜需要继续优化,如提供安全的、埋点Tarce上下文的线程池,提供其他多种埋点客户端等等。同时,当Trace信息变成海量后,怎么存储这些信息,以及快速分析,从中挖取更多有意义、有价值的信息,将会成为我们新的挑战。


     

    业务链路监控(Google Dapper)和ThreadLocal - 简书

    https://www.jianshu.com/p/48ff456fb643

    Trace信息是如何在链路内透传:

    Trace信息相当于在业务链路中的埋点信息

    如下图:链路的调用分2种,系统内部的调用通常是线程内的调用,而经过RPC、HTTP、异步消息调用都是不同系统(不同线程间)的调用

    线程内/线程间调用示例

    2种场景的Trace信息透传:

    • 线程/进程间传递使用参数传递:客户端调用服务端、异步消息调用属于信息从一个应用的线程转移到另外一个应用的线程,在2个线程之间传递Trace信息使用参数传递

    • 线程内传递使用ThreadLocal:线程内部的方法之间调用,无论调用了多少个方法,都是一个线程内部的调用,这些方法间传递Trace信息使用ThreadLocal

    • 线程间透传

      • HTTP:通过Http head或者body传递Trace信息。
      • RPC:通过自定义的rpc协议(根据rpc框架实现的不同,各个公司有不同的rpc协议实现)传递Trace信息。
      • MQ:通过消息头或者消息体携带Trace信息实现Trace信息从消息的生产者向消费者传递。
    • 线程内透传:

      • ThreadLocal:进入线程时,将Trace信息存储在ThreadLocal变量中,出线程时,从ThreadLocal变量中取出Trace信息,作为参数传递到下一个线程(应用系统)。

    ThreadLocal变量特殊的地方在于:对变量值的任何操作实际都是对这个变量在线程中的一份copy进行操作,不会影响另外一个线程中同一个ThreadLocal变量的值。

    一个线程中调用ThreadLocal变量的get/set方法获取和修改的是当前线程中存储的value,当前线程无法修改另外一个线程的存储的value,这就是ThreadLocal变量线程独立的原因。

    但是如果不同线程的value通过调用set方法指向同一个对象,ThreadLocal就丧失了线程独立性

    所以ThreadLocal线程独立的前提是:不要使用set方法设置value为同一个对象,ThreadLocal对象会自动在线程第一次调用get方法中调用initialValue()方法生成一个类型的实例作为value

    ThreadLocal变量的特点是:线程独立,生命周期和线程的生命周期一致。正是这2个特点,决定了它可以在分布式的业务链路监控系统中用于Trace信息的传输。


     

     

     


     


    Istio调用链埋点原理剖析—是否真的“零修改”? - InfoQ

    https://www.infoq.cn/article/pqy*PFPhox9OQQ9iCRTt

    Istio 提供了 Service Mesh 方式服务治理的完整的解决方案。正如其首页介绍,通过非侵入的方式提供了服务的连接、控制、保护和观测能力。包括智能控制服务间的流量和 API 调用;提供授权、认证和通信加密机制自动保护服务安全;通过开放策略来控制调用者对服务的访问;另外提供了可扩展丰富的调用链、监控、日志等手段来对服务与性能进行观测。即用户不用修改代码,就可以实现各种服务治理能力。

    较之其他系统和平台,Istio 比较明显的一个特点是服务运行的监控数据都可以动态获取和输出,提供了强大的调用链、监控和调用日志收集输出的能力。配合可视化工具,运维人员可以方便的看到系统的运行状况,并发现问题进而解决问题。而我们基本上不用在自己的代码里做任何修改来生成数据并对接各种监控、日志、调用链等后端。

    正如 Service Mesh 的诞生是为了解决大规模分布式服务访问的治理问题,调用链的出现也是为了对应于大规模的复杂的分布式系统运行中碰到的故障定位定界问题。大量的服务调用、跨进程、跨服务器,可能还会跨多个物理机房。无论是服务自身问题还是网络环境的问题导致调用上链路上出现问题都比较复杂,如何定位就比单进程的一个服务打印一个异常栈来找出某个方法要困难的多。需要有一个类似的调用链路的跟踪,经一次请求的逻辑规矩完整的表达出来,可以观察到每个阶段的调用关系,并能看到每个阶段的耗时和调用详细情况。

    Dapper, a Large-Scale Distributed Systems Tracing Infrastructure  描述了其中的原理和一般性的机制。模型中包含的术语也很多,理解最主要的两个即可:

    • Trace:一次完整的分布式调用跟踪链路。
    • Span:跨服务的一次调用; 多个 Span 组合成一次 Trace 追踪记录。

    调用链系统有很多实现,用的比较多的如 zipkin ,还有已经加入 CNCF 基金会并且的用的越来越多的Jaeger ,满足 Opentracing 语义标准的就有这么多

    一个完整的调用链跟踪系统,包括调用链埋点,调用链数据收集,调用链数据存储和处理,调用链数据检索(除了提供检索的 APIServer,一般还要包含一个非常酷炫的调用链前端)等若干重要组件。

    这里我们仅关注与应用相关的内容,即调用链埋点的部分,看下在 Istio 中是否能做到”无侵入“的调用链埋点。调用链的埋点是一个比起来记录日志,报个 metric 或者告警要复杂的多。根本原因其数据结构要相对复杂一些,为了能将在多个点上收集的关于一次调用的多个中间请求过程关联起来形成一个链。下面通过详析自带的典型例子来看下这里的细节。

    Istio 调用链埋点逻辑

    在 Istio 中,所有的治理逻辑的执行体都是和业务容器一起部署的 Envoy 这个 Sidecar,不管是负载均衡、熔断、流量路由还是安全、可观察性的数据生成都是在 Envoy 上。Sidecar 拦截了所有的流入和流出业务程序的流量,根据收到的规则执行执行各种动作。实际使用中一般是基于 K8S 提供的 InitContainer 机制,用于在 Pod 中执行一些初始化任务. InitContainer 中执行了一段 Iptables 的脚本。正是通过这些 Iptables 规则拦截 pod 中流量,并发送到 Envoy 上。Envoy 拦截到 Inbound 和 Outbound 的流量会分别作不同操作,执行上面配置的操作,另外再把请求往下发,对于 Outbound 就是根据服务发现找到对应的目标服务后端上;对于 Inbound 流量则直接发到本地的服务实例上。

    所以我们的重点是看下拦截到流量后 Sidecar 在调用链埋点怎么做的。

    Envoy 的埋点规则和在其他服务调用方和被调用方的对应埋点逻辑没有太大差别,甚至和一般 SDK 方式内置的调用链埋点逻辑也类似。

    以上过程也印证了前面我们提出的 Envoy 的埋点逻辑。可以看到过程中除了 Envoy 处理 Inbound 和 Outbound 流量时要执行对应的埋点逻辑外,每一步的调用要串起来,应用程序其实做了些事情。就是在将请求发给下一个服务时,需要将调用链相关的信息同样传下去,尽管这些 Trace 和 Span 的标识并不是它生成的。这样在出流量的 Proxy 向下一跳服务发起请求前才能判断并生成子 Span 并和原 Span 进行关联,进而形成一个完整的调用链。否则,如果在应用容器未处理 Header 中的 Trace,则 Sidecar 在处理请求时会创建根 Span,最终会形成若干个割裂的 Span,并不能被关联到一个 Trace 上,就会出现我们开始提到的问题。

    不断被问到两个问题来试图说明这个业务代码配合修改来实现调用链逻辑可能不必要:

    问题一:既然传入的请求上已经带了这些 Header 信息了,直接往下一直传不就好了吗?Sidecar 请求 APP 的时候带着这些 Header,APP 请求 Sidecar 时也带着这些 Header 不就完了吗?

    问题二:既然 TraceId 和 SpanId 是同一个 Sidecar 生成的,为什么要再费劲让 App 收到请求的时候解析下,发出请求时候再带着发出来传回给 Sidecar 呢?

    回答问题一,只需理解一点,这里的 App 业务代码是处理请求不是转发请求,即图上左边的 Request to Productpage 到 Productpage 中请求就截止了,要怎么处理完全是 Productpage 的服务接口的内容了,可以是调用本地处理逻辑直接返回,也可以是如示例中的场景构造新的请求调用其他的服务。右边的 Request from Productpage 完全是 Productpage 服务构造的发出的另外一个请求。

    回答问题二,需要理解当前 Envoy 是独立的 Listener 来处理 Inbound 和 Outbound 的请求。Inbound 只会处理入的流量并将流量转发到本地的服务实例上。而 Outbound 就是根据服务发现找到对应的目标服务后端上。除了在一个进程里外两个之间可以说没有任何关系。 另外如问题一描述,因为到 Outbound 已经是一个新构造的请求了,使得想维护一个 map 来记录这些 Trace 信息这种方案也变得不可行。

    回答问题一,只需理解一点,这里的 App 业务代码是处理请求不是转发请求,即图上左边的 Request to Productpage 到 Productpage 中请求就截止了,要怎么处理完全是 Productpage 的服务接口的内容了,可以是调用本地处理逻辑直接返回,也可以是如示例中的场景构造新的请求调用其他的服务。右边的 Request from Productpage 完全是 Productpage 服务构造的发出的另外一个请求。

    回答问题二,需要理解当前 Envoy 是独立的 Listener 来处理 Inbound 和 Outbound 的请求。Inbound 只会处理入的流量并将流量转发到本地的服务实例上。而 Outbound 就是根据服务发现找到对应的目标服务后端上。除了在一个进程里外两个之间可以说没有任何关系。 另外如问题一描述,因为到 Outbound 已经是一个新构造的请求了,使得想维护一个 map 来记录这些 Trace 信息这种方案也变得不可行。

    • Istio 的绝大多数治理能力都是在 Sidecar 而非应用程序中实现,因此是非侵入的;
    • Istio 的调用链埋点逻辑也是在 Sidecar 代理中完成,对应用程序非侵入,但应用程序需做适当的修改,即配合在请求头上传递生成的 Trace 相关信息。

    Dapper-Google大规模分布式跟踪系统 笔记

    Google 2010年发表了"Dapper - a Large-Scale Distributed Systems Tracing Infrastructure"论文介绍他们的分布式系统跟踪技术。因为最近有在学习opencensus,于是把它拜读一下并做笔记

    要解决的问题

    • 分布式系统服务很多,很复杂
    • 每个服务由不同的组开发,没有一个人能够理解所有的系统
    • 每个服务由不同的语言开发
    • 可能会涉及到上千台机器
    • 需要一个服务去跟踪请求,理解整体系统的瓶颈和实时的表现,如果一个请求太慢,如何快速的找到问题所在。

    设计目标

    • 效率要高
      • 对服务性能影响小
    • 对应用透明,侵入性小
      • 服务开发者不需要知道跟踪系统的存在
    • 能够大规模部署
    • 实时监控系统
      • 数据收集到数据展示之间时间尽量少(最好少于1分钟)。快速调试线上问题

    Dapper应用场景

    • 性能分析:跟踪请求延时,找到优化性能的地方,发现没有必要的请求
    • 正确性分析:发现一些只读请求因为有bug发给了数据库Master而不是Slave. 
    • 理解系统:全局优化系统,理解每个查询的整体代价
    • 测试新版本:发现性能问题
    • 用来分析长尾延时
    • 找到服务之间的依赖关系
    • 对公共的服务像文件系统,分析基于文件系统的服务的使用量

     

    APM(应用性能管理)与Dapper原理介绍- 谙忆-陈浩翔- CSDN博客

    https://blog.csdn.net/qq_26525215/article/details/88712368

    APM工具与传统的性能监控工具的区别在于,不仅仅提供一些零散的资源监控点和指标,其主要关注在系统内部执行、系统间调用的性能瓶颈分析,这样更有利于定位到问题的具体原因。
    APM致力于检测和诊断应用性能问题,从而能提供应用预期的服务水平。

    APM的发展历程

    目前APM的发展主要经历了前面的三个阶段:
    第一阶段:以网络监控基础设施为主,主要监控主机 的CPU 使用率、I/O、内存资源、网速等,主要以各类网络管理系统(NMS)和各种系统监控工具为代表。

    第二阶段:以监控各种基础组件为主,随着互联网的快速发展,为了降低应用开发难度,各种基础组件(如数据库、中间件等)开始大量涌现,所以这个时期应用性能管理主要是监控和管理各种基础组件的性能。

    第三阶段:以监控应用本身的性能为主, IT 运维管理的复杂度开始出现爆炸性的增长,应用性能管理的重点也开始聚焦于应用本身的性能与管理上。

    第四节阶段属于正在发展的阶段:
    云计算方兴未艾,而DevOps以及微服务的兴起对传统APM产生了很大的冲击,传统厂商也在做一些革新,也做一些微服务方面的尝试和云计算方面的尝试。
    随着Machine Learning、AI的技术的兴起,对定位故障、定位问题,也会起到一些帮助,基于大数据的分析的手段也会有一些帮助,目前市场上正在初步尝试阶段。

    2016年Gartner对APM的定义分为三个维度

    DEM-Digital experience monitoring:数字体验监控,浏览器及移动设备用户体验监控及利用主动拨测的实现的业务可用性及性能监控。
    ADTD-Application discovery, tracing and diagnostics:应用自动发现、追踪和故障诊断,自动发现应用之间的逻辑关系,自动建模、应用组件的深入监控及性能关联分析。
    AA-Application analytics:应用分析,通过机器学习,进行针对JAVA及.NET等应用的根源分析。
    --------------------- 
     

    好的APM应满足的条件

    总的来说,一个优秀的APM系统应该满足以下五个条件

    低消耗,高效率:被跟踪的系统为跟踪所付出的系统资源代价要尽量小,现在主流的APM对于系统资源的消耗在2.5%-5%左右,但是这个数值应该越小越好,因为在大规模的分布式系统下,一个单节点的资源是无法把控的,可能是超强配置,也可能是老爷机,只跑几个小服务,但是本身性能已经十分吃紧了,如果这时候跟踪应用再一跑,很可能这个节点就挂掉了,得不偿失。
    低侵入性,足够透明:作为跟踪系统,侵入性是不可能不存在的,关键这种侵入性要在哪个层面,如何在越底层的层面上侵入,对于开发者的感知和需要配合跟踪系统的工作就越少,如果在代码层面就需要进行侵入,那对于本身业务就比较复杂的应用来说,代码就更加冗余复杂了,也不利于开发者快节奏的开发。
    灵活的延展性:不能随着微服务和集群规模的扩大而使分布式跟踪系统瘫痪,要能够充分考虑到未来分布式服务的规模,跟踪系统至少要在未来几年内完全吃得消。
    跟踪数据可视化和迅速反馈:要有可视化的监控界面,从跟踪数据收集、处理到结果的展现尽量做到快速,就可以对系统的异常状况作出快速的反应
    持续的监控: 要求分布式跟踪系统必须是7x24小时工作的,否则将难以定位到系统偶尔抖动的行为
    --------------------- 

    如何实现应用级透明?

    在google的环境中,所有的应用程序使用相同的线程模型、控制流和RPC系统,既然不能让工程师写代码记录日志,那么就只能让这些线程模型、控制流和RPC系统来自动帮助工程师记录日志了。

    举个例子,几乎所有的google进程间通信是建立在一个用C++和JAVA开发的RPC框架上,dapper把跟踪植入这个框架,span的ID和跟踪的ID会从客户端发送到服务端,这样工程师也就不需要关心应用实现层次。
    --------------------- 

    跟踪损耗

    跟踪系统的成本由两部分组成:

    • 正在被监控的系统在生成追踪和收集追踪数据的消耗导致系统性能下降
    • 需要使用一部分资源来存储和分析跟踪数据。虽然你可以说一个有价值的组件植入跟踪带来一部分性能损耗是值得的,我们相信如果基本损耗能达到可以忽略的程度,那么对跟踪系统最初的推广会有极大的帮助。

    接下来展现一下三个方面:Dapper组件操作的消耗,跟踪收集的消耗,以及Dapper对生产环境负载的影响。我们还介绍了Dapper可调节的采样率机制如何帮我们处理低损耗和跟踪代表性之间的平衡和取舍。


    --------------------- 

    跟踪损耗

    跟踪系统的成本由两部分组成:

    • 正在被监控的系统在生成追踪和收集追踪数据的消耗导致系统性能下降
    • 需要使用一部分资源来存储和分析跟踪数据。虽然你可以说一个有价值的组件植入跟踪带来一部分性能损耗是值得的,我们相信如果基本损耗能达到可以忽略的程度,那么对跟踪系统最初的推广会有极大的帮助。

    接下来展现一下三个方面:Dapper组件操作的消耗,跟踪收集的消耗,以及Dapper对生产环境负载的影响。我们还介绍了Dapper可调节的采样率机制如何帮我们处理低损耗和跟踪代表性之间的平衡和取舍。

    生成跟踪的损耗

    生成跟踪的开销是Dapper性能影响中最关键的部分,因为收集和分析可以更容易在紧急情况下被关闭。Dapper运行库中最重要的跟踪生成消耗在于创建和销毁span和annotation,并记录到本地磁盘供后续的收集。根span的创建和销毁需要损耗平均204纳秒的时间,而同样的操作在其他span上需要消耗176纳秒。时间上的差别主要在于需要在跟span上给这次跟踪分配一个全局唯一的ID。

    如果一个span没有被采样的话,那么这个额外的span下创建annotation的成本几乎可以忽略不计,他由在Dapper运行期对ThreadLocal查找操作构成,这平均只消耗9纳秒。如果这个span被计入采样的话,会用一个用字符串进行标注–在图4中有展现–平均需要消耗40纳秒。这些数据都是在2.2GHz的x86服务器上采集的。

    在Dapper运行期写入到本地磁盘是最昂贵的操作,但是他们的可见损耗大大减少,因为写入日志文件和操作相对于被跟踪的应用系统来说都是异步的。不过,日志写入的操作如果在大流量的情况,尤其是每一个请求都被跟踪的情况下就会变得可以察觉到。

    跟踪收集的消耗

    谷歌的统计数据:
    最坏情况下,Dapper收集日志的守护进程在高于实际情况的负载基准下进行测试时的cpu使用率:没有超过0.3%的单核cpu使用率。
    限制了Dapper守护进程为内核scheduler最低的优先级,以防在一台高负载的服务器上发生cpu竞争。
    Dapper也是一个带宽资源的轻量级的消费者,每一个span在我们的仓库中传输只占用了平均426的byte。作为网络行为中的极小部分,Dapper的数据收集在Google的生产环境中的只占用了0.01%的网络资源。

    图3:Dapper守护进程在负载测试时的CPU资源使用率


    在生产环境下对负载的影响

    每个请求都会利用到大量的服务器的高吞吐量的线上服务,这是对有效跟踪最主要的需求之一;这种情况需要生成大量的跟踪数据,并且他们对性能的影响是最敏感的。在表2中我们用集群下的网络搜索服务作为例子,我们通过调整采样率,来衡量Dapper在延迟和吞吐量方面对性能的影响。

    图4:网络搜索集群中,对不同采样率对网络延迟和吞吐的影响。延迟和吞吐的实验误差分别是2.5%和0.15%。


    我们看到,虽然对吞吐量的影响不是很明显,但为了避免明显的延迟,跟踪的采样还是必要的。然而,延迟和吞吐量的带来的损失在把采样率调整到小于1/16之后就全部在实验误差范围内。在实践中,我们发现即便采样率调整到1/1024仍然是有足够量的跟踪数据的用来跟踪大量的服务。保持Dapper的性能损耗基线在一个非常低的水平是很重要的,因为它为那些应用提供了一个宽松的环境使用完整的Annotation API而无惧性能损失。使用较低的采样率还有额外的好处,可以让持久化到硬盘中的跟踪数据在垃圾回收机制处理之前保留更长的时间,这样为Dapper的收集组件给了更多的灵活性。

    采样

    分布式跟踪系统的实现要求是性能低损耗的,尤其在生产环境中分布式跟踪系统不能影响到核心业务的性能。 Google也不可能每次请求都跟踪的,所以要进行采样,每个应用和服务可以自己设置采样率。采样率应该是每个应用自己的配置里配置的,这样每个应用可以动态调整,特别是刚应用刚上线使可以适当调高采样率。

    一般在系统峰值流量很大的情况下,只需要采样其中很小一部分请求,例如1/1000的采样率,即分布式跟踪系统只会在1000次请求中采样其中的某一次。

    可变采样

    任何给定进程的Dapper的消耗和每个进程单位时间的跟踪的采样率成正比。Dapper的第一个生产版本在Google内部的所有进程上使用统一的采样率,为1/1024。这个简单的方案是对我们的高吞吐量的线上服务来说是非常有用,因为那些感兴趣的事件(在大吞吐量的情况下)仍然很有可能经常出现,并且通常足以被捕捉到。

    然而,在较低的采样率和较低的传输负载下可能会导致错过重要事件,而想用较高的采样率就需要能接受的性能损耗。对于这样的系统的解决方案就是覆盖默认的采样率,这需要手动干预的,这种情况是我们试图避免在dapper中出现的。

    我们在部署可变采样的过程中,参数化配置采样率时,不是使用一个统一的采样方案,而是使用一个采样期望率来标识单位时间内采样的追踪。这样一来,低流量低负载自动提高采样率,而在高流量高负载的情况下会降低采样率,使损耗一直保持在控制之下。实际使用的采样率会随着跟踪本身记录下来,这有利于从Dapper的跟踪数据中准确的分析。

    应对积极采样(Coping with aggressive sampling)

    新的Dapper用户往往觉得低采样率–在高吞吐量的服务下经常低至0.01%–将会不利于他们的分析。我们在Google的经验使我们相信,对于高吞吐量服务,积极采样(aggressive sampling)并不妨碍最重要的分析。如果一个显着的操作在系统中出现一次,他就会出现上千次。低吞吐量的服务–也许是每秒请求几十次,而不是几十万–可以负担得起跟踪每一个请求,这是促使我们下决心使用自适应采样率的原因。

    在收集过程中额外的采样

    上述采样机制被设计为尽量减少与Dapper运行库协作的应用程序中明显的性能损耗。Dapper的团队还需要控制写入中央资料库的数据的总规模,因此为达到这个目的,我们结合了二级采样。

    目前我们的生产集群每天产生超过1TB的采样跟踪数据。Dapper的用户希望生产环境下的进程的跟踪数据从被记录之后能保存至少两周的时间。逐渐增长的追踪数据的密度必须和Dapper中央仓库所消耗的服务器及硬盘存储进行权衡。对请求的高采样率还使得Dapper收集器接近写入吞吐量的上限。

    为了维持物质资源的需求和渐增的Bigtable的吞吐之间的灵活性,我们在收集系统自身上增加了额外的采样率的支持。我们充分利用所有span都来自一个特定的跟踪并分享同一个跟踪ID这个事实,虽然这些span有可能横跨了数千个主机。对于在收集系统中的每一个span,我们用hash算法把跟踪ID转成一个标量Z,这里0<=Z<=1。如果Z比我们收集系统中的系数低的话,我们就保留这个span信息,并写入到Bigtable中。反之,我们就抛弃他。通过在采样决策中的跟踪ID,我们要么保存、要么抛弃整个跟踪,而不是单独处理跟踪内的span。我们发现,有了这个额外的配置参数使管理我们的收集管道变得简单多了,因为我们可以很容易地在配置文件中调整我们的全局写入率这个参数。

    如果整个跟踪过程和收集系统只使用一个采样率参数确实会简单一些,但是这就不能应对快速调整在所有部署的节点上的运行期采样率配置的这个要求。我们选择了运行期采样率,这样就可以优雅的去掉我们无法写入到仓库中的多余数据,我们还可以通过调节收集系统中的二级采样率系数来调整这个运行期采样率。Dapper的管道维护变得更容易,因为我们就可以通过修改我们的二级采样率的配置,直接增加或减少我们的全局覆盖率和写入速度。

    ???上面这段没来及看懂~~

    最重要的Dapper的不足

    合并的影响:我们的模型隐含的前提是不同的子系统在处理的都是来自同一个被跟踪的请求。在某些情况下,缓冲一部分请求,然后一次性操作一个请求集会更加有效。(比如,磁盘上的一次合并写入操作)。在这种情况下,一个被跟踪的请求可以看似是一个大型工作单元。此外,当有多个追踪请求被收集在一起,他们当中只有一个会用来生成那个唯一的跟踪ID,用来给其他span使用,所以就无法跟踪下去了。我们正在考虑的解决方案,希望在可以识别这种情况的前提下,用尽可能少的记录来解决这个问题。
    跟踪批处理负载:Dapper的设计,主要是针对在线服务系统,最初的目标是了解一个用户请求产生的系统行为。然而,离线的密集型负载,例如符合MapReduce模型的情况,也可以受益于性能挖潜。在这种情况下,我们需要把跟踪ID与一些其他的有意义的工作单元做关联,诸如输入数据中的键值(或键值的范围),或是一个MapReduce shard。
    寻找根源:Dapper可以有效地确定系统中的哪一部分致使系统整个速度变慢,但并不总是能够找出问题的根源。例如,一个请求很慢有可能不是因为它自己的行为,而是由于队列中其他排在它前面的(queued ahead of)请求还没处理完。程序可以使用应用级的annotation把队列的大小或过载情况写入跟踪系统。此外,如果这种情况屡见不鲜,那么在ProfileMe中提到的成对的采样技术可以解决这个问题。它由两个时间重叠的采样率组成,并观察它们在整个系统中的相对延迟。
    记录内核级的信息:一些内核可见的事件的详细信息有时对确定问题根源是很有用的。我们有一些工具,能够跟踪或以其他方式描述内核的执行,但是,想用通用的或是不那么突兀的方式,是很难把这些信息到捆绑到用户级别的跟踪上下文中。我们正在研究一种妥协的解决方案,我们在用户层面上把一些内核级的活动参数做快照,然后绑定他们到一个活动的span上。
    MapReduce模型

    MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)“和"Reduce(归约)”,是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。 当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。

    ProfileMe提到的成对采样技术

    >>>?

    英文论文地址:https://www.cs.tufts.edu/comp/150PAT/tools/dcpi/micro30.pdf
    --------------------- 
    版权声明:本文为CSDN博主「谙忆」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_26525215/article/details/88712368

    主流APM框架对比:

    方案 依赖 实现方式 存储 JVM监控 trace查询 侵入 部署成本
    Pinpoint Java 6,7,8 maven3+ Hbase0.94+ java探针,字节码增强 HBase 支持 需要二次开发 最低 较高
    SkyWalking Java 6,7,8 maven3.0+ nodejs zookeeper elasticsearch java探针,字节码增强 elasticsearch , H2 ,mysql,TIDN,Sharding Sphere 支持 支持
    Zipkin Java 6,7,8 Maven3.2+ rabbitMQ 拦截请求,发送(HTTP,mq)数据至zipkin服务 内存 , mysql , Cassandra , Elasticsearch 不支持 支持 高,需要开发
    CAT Java 6 7 8、Maven 3+ MySQL 5.6 5.7、Linux 2.6+ hadoop可选 代码埋点(拦截器,注解,过滤器等) mysql , hdfs 不支持 支持 高,需要埋点

    基于对程序源代码和配置文件的低侵入考虑,推荐的选型顺序依次是 Pinpoint > SkyWalking > Zipkin > CAT

    Pinpoint:基本不用修改源码和配置文件,只要在启动命令里指定javaagent参数即可,对于运维人员来讲最为方便;
    SkyWalking:不用修改源码,需要修改配置文件;
    Zipkin:需要对Spring、web.xml之类的配置文件做修改,相对麻烦一些;
    CAT:因为需要修改源码设置埋点,因此基本不太可能由运维人员单独完成,而必须由开发人员的深度参与了;

    相对于传统的监控软件(Zabbix之流)的区别,APM跟关注在对于系统内部执行、系统间调用的性能瓶颈分析,这样更有利于定位到问题的具体原因,而不仅仅像传统监控软件一样只提供一些零散的监控点和指标,就算告警了也不知道问题是出在哪里。

    总结

    主流APM工具为了更好地进行推广,主要采用了侵入程度低的方式完成对应用代码的改造。并且为了应对云计算、微服务、容器化的迅速发展与应用带来的APM监控的数据的海量增长的趋势,数据落地方式也主要以海量存储数据库为主。

    未来在数据分析和性能分析方面,大数据和机器学习将在APM领域发挥重要的作用,APM的功能也将从单一的资源监控和应用监控,向异常检测、性能诊断、未来预测等自动化、智能化等方向发展。

    后面有时间会进行整理一篇JavaAgent机制以及ASM字节码技术的文章。
    --------------------- 
    版权声明:本文为CSDN博主「谙忆」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_26525215/article/details/88712368


  • 相关阅读:
    Java泛型类和泛型方法
    Java泛型解析
    Reflection and array
    再叙Java反射
    Java Reflection(getXXX和getDeclaredXXX)
    Java反射(Reflection)
    MySQL Block Nested Loop and Batched Key Access Joins(块嵌套循环和批量Key访问连接)
    jQuery学习之jQuery Ajax用法详解
    request和request.form和request.querystring的区别
    浏览器中event.srcElement和event.target的兼容性问题
  • 原文地址:https://www.cnblogs.com/cx2016/p/12926174.html
Copyright © 2020-2023  润新知