初探分布式链路追踪

原创
01/29 16:20
阅读数 1.7K




本篇文章,主要介绍应用如何正确使用日志系统,帮助用户从依赖、输出、清理、问题排查、报警等各方面全面掌握。


可观测性


可观察性不单是一套理论框架,而且并不强制具体的技术规格。其核心在于鼓励团队内化可观察性的理念,并确保由研发人员构建的应用程序具备可观察性。在学术领域中,尽管“可观测性”这一术语是近年来从控制理论中引进的新词,但实际上,它在计算机科学领域已有深厚的实践基础。学者们通常会把可观测性细化为三个更具体的研究方向:事件日志、链路追踪和聚合度量。这三个领域虽然各有侧重点,但并非完全孤立,它们之间存在着天然的交集与互补性。


  1. 日志(Logging),展现的是应用运行而产生的事件或者程序在执行的过程中间产生的一些日志,可以详细解释系统的运行状态,但是存储和查询需要消耗大量的资源。所以往往使用过滤器减少数据量。

  2. 度量(Metrics),是一种聚合数值,存储空间很小,可以观察系统的状态和趋势,但对于问题定位缺乏细节展示。这个时候使用等高线指标等多维数据结构来增强对于细节的表现力。例如统计一个服务的 TBS 的正确率、成功率、流量等,这是常见的针对单个指标或者某一个数据库的。

  3. 追踪(Tracing),面向的是请求,可以轻松分析出请求中异常点,但与日志有相同的问题就是资源消耗较大。通常也需要通过采样的方式减少数据量。比如一次请求的范围,也就是从浏览器或者手机端发起的任何一次调用,一个流程化的东西,需要轨迹去追踪。


在工业界,历经长期的竞争,日志和度量领域的主导技术已逐渐明朗。日志收集与分析方面,Elastic Stack(亦称为ELK Stack)已成为广泛采纳的技术选择。然而,追踪技术的发展路径与日志和度量领域显然不同。追踪技术高度依赖于特定的网络协议和编程语言。服务间是通过 HTTP 还是 gRPC 通信,会直接决定追踪实施的具体方法。同样,服务是否采用 Java、Golang、还是 Node.js 编写,也会影响到如何追踪进程内调用堆栈。这种依赖性使得追踪工具通常需要以插件或探针的形式进行深度集成,因而具有一定的侵入性。此外,由于追踪技术需要适应不同的语言和网络环境,追踪领域很难形成单一的主导者。相反,市场上存在多样化的产品,旨在满足各种不同技术栈的追踪需求。


本文主要探讨其中的追踪下日志内容。


链路追踪

分布式链路追踪公认的起源是 Google 在 2010 年发表的论文《Dapper : a Large-Scale Distributed Systems Tracing Infrastructure》,这篇论文介绍了 Google 从 2004 年开始使用的分布式追踪系统 Dapper 的实现原理。


论文 Dapper : a Large-Scale Distributed Systems Tracing Infrastructure 》地址:https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/papers/dapper-2010-1.pdf

  基本原理



分布式链路追踪的基本原理就是在分布式应用的接口方法上设置一些观察点,然后在入口节点给每个请求分配一个全局唯一的标识 TraceId。当请求流经这些观察点时就会记录一行对应的链路日志(包含链路唯一标识,接口名称,时间戳,主机信息等)。最后通过 TraceId 将一次请求的所有链路日志进行组装,就可以还原出该次请求的链路轨迹。


分布式链路追踪实现请求回溯的关键点有两个:一是低成本、高质量的观察点设置,也就是链路插桩,确保追踪的信息足够丰富,能够快速定位异常根因;二是保证链路上下文在不同环境下都能够完整透传,避免出现上下文丢失导致的断链现象。


  基础术语


虽然分布式链路追踪的实现方式多种多样,但是仍然有一些基础术语在业界具备广泛的共识。

  • Trace


一条 Trace 代表一次入口请求在 IT 系统内的完整调用轨迹及其关联数据集合。其中,全局唯一的链路标识 TraceId,是最具代表的一个属性。通过 TraceId 才能将同一个请求分散在不同节点的链路数据准确的关联起来,实现请求粒度的“确定性关联”价值。


以集团的Eagleeye设计的traceId设计为例,根据这个id,可以知道这个请求在2022-10-18 10:10:40发出,被11.15.148.83机器上进程号为14031的Nginx(对应标识位e)接收到。其中的四位原子递增数从0-9999,目的是为了防止单机并发造成traceId碰撞。


  • Span


Span是一个操作,它代表系统中一个逻辑运行单元。Span之间通过嵌套或者顺序排列建立因果关系。Span包含以下对象

  1. Operation Name:描述了当前接口的行为语义,比如 /api/createOrder 代表执行了一次创建订单的动作。

  2. SpanId/ParentSpanId:接口调用的层级标识,用于还原 Trace 内部的层次调用关系。

  3. Start/FinishTime:接口调用的开始和结束时间,二者相减就是该次调用的耗时。

  4. StatusCode:响应状态,标识当次调用是成功或失败。

  5. Tags & Events:调用附加信息,详见下面的描述。



其中最重要的是SpanId的设计。同样以阿里的Eagleeye的spanId(rpcId)设计为例,Eagleeye设计了RpcId来区别同一个调用链下多个网络调用的顺序和嵌套层次,用0.X1.X2.X3.....Xi来表示,根节点的RpcId固定从0开始,id的位数("."的数量)表示了Span在这棵树中的层级,Id最后一位表示了Span在这一层级中的顺序。那么给定同一个Trace中的所有RpcId,便可以很容易还原出一个完成的调用链:
- 0  - 0.1    - 0.1.1    - 0.1.2      - 0.1.2.1  - 0.2    - 0.2.1  - 0.3    - 0.3.1      - 0.3.1.1    - 0.3.2

  • Tags


如果需要进一步记录请求的行为特征,可以使用 Tags 来扩展语义。Tags 是一组由 {Key:Value} 组成的键值对集合,描述这一次接口调用的具体属性。比如Eagleeye里就有「Tags」的实现(UserData),阿里的全链路压测标识就是通过UserData里传递实现的。


在分布式追踪的上下文中,一个Trace可被视为一个有向无环图,其中每个Span代表图中的一个节点,而节点之间的连接则表示为链接。观察这样的图表,我们可以看到一个单独的Span节点可能拥有多条链接,这表明它具有多个子Span。这种结构揭示了各个操作间的父子或者因果关系,从而使得追踪整个请求的流程成为可能。


Trace定义了Span间两种基本关系:

  1. ChildOf:表示父 Span 在一定程度上依赖子 Span。

  2. FollowsFrom:表示父 Span 完全不依赖其子Span 的结果。


  链路追踪的挑战


  • 日志存储


本地存储埋点数据涉及磁盘操作,而磁盘I/O速度通常较慢。在高并发的环境中,如果采用同步刷新磁盘的方式,可能会对原有业务的性能造成显著影响。因此,在链路追踪系统中进行数据埋点时,应当尽量减少对系统的性能损耗,确保对原业务的逻辑和性能具有无侵入性。这意味着追踪系统的设计需要尽量轻量化,并采取策略如异步处理、缓存等方法来降低对业务性能的影响。


并发环形队列


集团的Eagleeye采用并发环形队列存储Trace数据,如下图所示:


环形队列确实是日志框架中实现异步日志写入的一种常用技术。在这种队列中,存在两个关键的指针,即读指针(take)和写指针(put)。

  1. 读指针(take): 它指向队列中的最旧数据,即下一条待消费的数据。

  2. 写指针(put): 它指向队列中下一个将要存放新数据的位置。


这个队列被设计为支持原子性的读写操作,以保证在多线程环境中数据的一致性。这意味着,当一个线程在执行读或写操作时,其他线程不能同时修改被操作的数据项。


读指针和写指针按同一方向(如时钟方向)移动,随着数据的不断生产和消费,这两个指针在队列中循环前进。然而,如果生产数据的速度持续超过消费速度,写指针最终会“追上”读指针,这种情况被称为“套圈”。


当出现套圈现象时,队列将根据预设的策略来处理新产生的数据。常见的处理策略包括:

  1. 丢弃即将写入的新数据,以保护队列中已存在的数据不被覆盖。

  2. 覆盖队列中最旧的数据,这意味着最先入队的数据将被新数据取代,这种策略适用于对最新数据保持最高优先级的场景。


选择哪种策略取决于具体应用场景的需求和对数据丢失的容忍度。环形队列的这种设计使得它在处理高并发日志数据时非常高效,能够在保证性能的同时减少资源占用。


Skywalking在实现上有所区别,采用分区的QueueBuffer存储Trace数据,多个消费线程通过Driver平均分配到各个QueueBuffer上进行数据消费:


在高性能的应用场景中,队列的实现方式对于整体性能有显著的影响。QueueBuffer 通常有两种不同的实现方式:

  1. 基于 JDK 的阻塞队列:这种实现方式利用了 Java 标准库中的 java.util.concurrent 包下的阻塞队列,例如 ArrayBlockingQueue 或 LinkedBlockingQueue。这些队列内部处理了线程同步的逻辑,使得在多线程环境下生产者和消费者可以安全地进行并发操作。基于 JDK 的阻塞队列通常用于服务端,因为服务端可以承受相对较重的负载,并且更注重于稳定性和线程安全。

  2. 普通数组 + 原子下标:这种实现方式使用了一个普通的数组,并结合了原子操作的下标(如 AtomicInteger)用于记录读写的位置。这种队列不是阻塞的,因此,当队列满时,它需要显式地处理生产者的等待逻辑,当队列空时,需要处理消费者的等待逻辑。SkyWalking 的 Agent 端采用这种方式,因为它相对更轻量级,且在高吞吐量场景下提供了更高的性能。Agent 端的性能尤其重要,因为它直接运行在应用程序内部,性能的任何抖动都可能影响到应用程序本身的性能。


在普通数组 + 原子下标的实现中,有一些值得关注的点:

  1. 原子性操作:通过使用原子类(如 AtomicInteger),可以确保在并发场景下修改下标时的线程安全性,避免了锁的开销。

  2. 非阻塞算法:由于不使用阻塞队列,这种实现可以采用非阻塞算法来优化性能,尤其是在高并发的生产者-消费者模型中。

  3. 回绕逻辑:当数组达到其末端时,读写下标需要回绕到数组的开始,形成一个环状结构,这就是所谓的环形缓冲区。

  4. 饱和策略:需要定义当队列满时的行为,是等待、丢弃数据还是覆盖旧数据。

  5. 总的来说,不同的 QueueBuffer 实现方式适用于不同的使用场景


  • 日志传输



鹰眼(EagleEye)和SkyWalking采用了不同的数据采集及传输策略,每种方法都有其适用场景和相应的权衡。


鹰眼系统首先将数据存储到本地日志文件中,然后利用一个代理(agent)来收集这些日志并将其传输到服务端。这种基于日志加边车代理的方法使得数据采集可以在不直接干扰主应用逻辑的情况下进行,但这也意味着需要处理日志文件的管理和代理的维护。


相比之下,SkyWalking提供了gRPC和Kafka两种基于服务的数据传输方式,这使得数据可以更实时地传输到处理后端。gRPC是一种高性能的远程过程调用(RPC)框架,Kafka是一个分布式流处理平台,两者都能有效地处理大量的数据流。


从架构上看,SkyWalking采取了分离应用层埋点和中间件代码的策略,这在某种程度上实现了应用级别的透明性。用户不需要在应用代码中直接管理追踪逻辑,而是依赖中间件层的插件来自动完成数据的采集。然而,这种方式意味着当中间件需要升级时,相关的插件也可能需要更新,这可能在维护方面引入一些挑战。


鹰眼的埋点方法由专门的中间件团队来维护,对于上层应用来说同样是透明的,这意味着应用开发者不需要关心数据采集的具体实现细节。这种方法非常适合阿里巴巴集团内部的环境,因为它能够提供统一的标准和容易控制的集成点。


总的来说,这两种系统提供了不同的解决方案,各自的优点和挑战都值得在选择和实施追踪系统时予以考虑。


  • 数据埋点


在分布式链路追踪系统中,方法增强(通常称为“埋点”)指的是在代码中插入额外的逻辑来捕获和记录执行信息,这是实现追踪的核心技术。Dapper,谷歌的分布式追踪系统,提出的设计原则要求方法增强必须同时满足应用级透明性和低开销这两个关键条件:

  1. 应用级透明:开发者不需要修改业务代码或仅需要极少的修改即可实现埋点,这意味着追踪逻辑对应用层是不可见或几乎不可见的。

  2. 低开销:埋点操作对系统的性能影响应当尽可能小,以避免追踪逻辑本身成为系统性能的瓶颈。


现在,我们来比较阿里巴巴的EagleEye与开源项目SkyWalking在埋点技术上的不同做法:


硬编码


EagleEye:阿里巴巴的EagleEye通过中间件层进行数据采集和追踪,通常这由集团的中间件团队负责维护。这种中间件集成方式允许上层应用无需知晓具体的追踪逻辑,因此在应用级别上保持了较高的透明度。然而,这种方法可能会在中间件升级时需要同步更新埋点逻辑,增加了维护的复杂度。


字节码增强


SkyWalking:SkyWalking提供了自动插桩的能力,通常通过Java Agent技术在运行时不侵入地修改字节码,插入追踪代码。这种方式对业务代码完全透明,开发者无需修改任何业务逻辑即可实现埋点。同时,SkyWalking旨在保持低开销,以减少对应用性能的影响。由于它是一个开源项目,SkyWalking更容易适应多样化的技术栈和快速发展的生态。


两种方法各有优势和劣势,EagleEye的中间件集成方式适合阿里巴巴这样拥有统一技术栈和强大内部支持的大型企业环境。而SkyWalking的自动插桩技术则提供了灵活性和广泛的社区支持,更适合多变的技术环境和需要快速适应新技术的公司。选择哪种埋点方式取决于企业的具体需求、技术栈的复杂性以及维护能力。



  • 跨线程传递


在多线程并发调用环境下的数据链路埋点也是一个值得关注问题,当一个服务考虑性能问题可能会起多个线程同时调用其他不同的模块。链路系统如何保证这些调用还是符合 openTrace 规范,保证 traceId 和 spanId 有序。


目前Java生态下,普遍的思路是用父线程的ThreadLocal中拿到保存的trace信息,然后作为参数传递给子线程,子线程在初始化的时候设置trace信息来避免丢失。


值得关注的是,集团提供了InheritableThreadLocals的子类TransmittableThreadLocal,解决了InheritableThreadLocal只能再new Thread的时候传递本地变量,无法应用到线程池的问题。可以应用来作链路追踪,传递变量等用途。美团也实现了类似的子类TransmissibleThreadLocal。



日志收集和分析


日志用来记录系统运行期间发生过的离散事件,在目前复杂的分布式系统中,很难只依靠 tail、grep、awk 来从单台机器的日志中挖掘信息了,往往还要有专门的全局查询和可视化功能。此时,从打印日志到分析查询之间,还隔着收集、缓冲、聚合、加工、索引、存储等若干个步骤,如下图所示:


提到日志实时分析,大部分人第一想到是社区很火ELK Stack。ELK方案上手难度小、开源材料众多、在社区中有大量的使用案例。于此同时,阿里巴巴集团也推出了对日志场景的解决方案产品日志服务(SLS/Log) 。下面将先后介绍这两种框架并分析优劣。


  开源的ELK方案


ELK Stack 是一个缩略词,用来描述由三个常见项目组成的堆栈:Elasticsearch、Logstash 和 Kibana。ELK Stack 通常被称为 Elasticsearch,作用是聚合来自所有系统和应用程序的日志,分析这些日志,并创建可视化来进行应用程序和基础设施监控、更快的故障排除、安全分析等。


E = Elasticsearch 

Elasticsearch 是在 Apache Lucene 上构建的分布式搜索和分析引擎。对各种语言、高性能和无架构 JSON 文档的支持使 Elasticsearch 成为各种日志分析和搜索使用案例的理想选择。 


L = Logstash

Logstash 是一个开源数据摄取工具,允许从各种来源收集数据,转换数据,并将数据发送到希望的目标。


K = Kibana

Kibana 是一种数据可视化和挖掘工具,可以用于日志和时间序列分析、应用程序监控和运营智能使用案例。


  • 收集与缓冲


写日志是在服务节点中进行的,但我们不可能在每个节点都单独建设日志查询功能。这不是资源或工作量的问题,而是分布式系统处理一个请求要跨越多个服务节点,为了能看到跨节点的全部日志,就要有能覆盖整个链路的全局日志系统。这个需求决定了每个节点输出日志到文件后,必须将日志文件统一收集起来集中存储、索引,由此便催生了专门的日志收集器。


最初,ELK 中日志收集与下一节要讲的加工聚合的职责都是由 Logstash 来承担的,Logstash 除了部署在各个节点中作为收集的客户端(Shipper)以外,它还同时设有独立部署的节点,扮演归集转换日志的服务端(Master)角色。Logstash 有良好的插件化设计,收集、转换、输出都支持插件化定制,应对多重角色本身并没有什么困难。但是 Logstash 与它的插件是基于 JRuby 编写的,要跑在单独的 Java 虚拟机进程上,而且 Logstash 的默认的堆大小就到了 1GB。对于归集部分(Master)这种消耗并不是什么问题,但作为每个节点都要部署的日志收集器就显得太过负重了。后来,Elastic.co 公司将所有需要在服务节点中处理的工作整理成以Libbeat为核心的Beats 框架,并使用 Golang 重写了一个功能较少,却更轻量高效的日志收集器Filebeat。


日志不追求绝对的完整精确,只追求在代价可承受的范围内保证尽可能地保证较高的数据质量。一种最常用的缓解压力的做法是将日志接收者从 Logstash 和 Elasticsearch 转移至抗压能力更强的队列缓存,譬如在 Logstash 之前架设一个 Kafka 或者 Redis 作为缓冲层,面对突发流量,Logstash 或 Elasticsearch 处理能力出现瓶颈时自动削峰填谷,甚至当它们短时间停顿,也不会丢失日志数据。


  • 加工与聚合


将日志集中收集之后,存入 Elasticsearch 之前,一般还要对它们进行加工转换和聚合处理。这是因为日志是非结构化数据,一行日志中通常会包含多项信息,如果不做处理,那在 Elasticsearch 就只能以全文检索的原始方式去使用日志,既不利于统计对比,也不利于条件过滤。


Logstash 的基本职能是把日志行中的非结构化数据,通过 Grok 表达式语法转换为上面表格那样的结构化数据,进行结构化的同时,还可能会根据需要,调用其他插件来完成时间处理(统一时间格式)、类型转换(如字符串、数值的转换)、查询归类(譬如将 IP 地址根据地理信息库按省市归类)等额外处理工作,然后以 JSON 格式输出到 Elasticsearch 中(这是最普遍的输出形式,Logstash 输出也有很多插件可以具体定制不同的格式)。有了这些经过 Logstash 转换,已经结构化的日志,Elasticsearch 便可针对不同的数据项来建立索引,进行条件查询、统计、聚合等操作的了。


  • 存储与查询


Elasticsearch 是整个 Elastic Stack 技术栈的核心,其他步骤的工具,如 Filebeat、Logstash、Kibana 都有替代品,有自由选择的余地,唯独 Elasticsearch 在日志分析这方面完全没有什么值得一提的竞争者,几乎就是解决此问题的唯一答案。核心原因是 Elasticsearch 的优势正好与日志分析的需求完美契合:
  1. 数据特征的角度看,日志是典型的基于时间的数据流。日志虽然增长速度很快,但已写入的数据几乎没有再发生变动的可能。以按日索引为例,由于你能准确地预知明天、后天的日期,因此全部索引都可以预先创建,这免去了动态创建的寻找节点、创建分片、在集群中广播变动信息等开销
  2. 从数据价值的角度看,日志基本上只会以最近的数据为检索目标,随着时间推移,早期的数据将逐渐失去价值。这点决定了可以很容易区分出冷数据和热数据,进而对不同数据采用不一样的硬件策略。
  3. 从数据使用的角度看,分析日志很依赖全文检索和即席查询,对实时性的要求是处于实时与离线两者之间的“近实时”,即不强求日志产生后立刻能查到,但也不能接受日志产生之后按小时甚至按天的频率来更新,这些检索能力和近实时性,也正好都是 Elasticsearch 的强项。

Elasticsearch 只提供了 API 层面的查询能力,它通常搭配同样出自 Elastic.co 公司的 Kibana 一起使用,可以将 Kibana 视为 Elastic Stack 的 GUI 部分。Kibana 尽管只负责图形界面和展示,但它提供的能力远不止让你能在界面上执行 Elasticsearch 的查询那么简单。Kibana 宣传的核心能力是“探索数据并可视化”,即把存储在 Elasticsearch 中的数据被检索、聚合、统计后,定制形成各种图形、表格、指标、统计,以此观察系统的运行状态,找出日志事件中潜藏的规律和隐患。

  阿里的SLS方案


阿里云日志服务 SLS 基于阿里云自研灵活索引的一站式云原生可观测数据分析平台。为Log/Metric/Trace 等数据提供大规模、低成本、实时平台化服务。一站式提供数据采集、加工、分析、告警可视化与投递功能。

  • 收集与缓存


Logtail是集团云提供的日志服务(SLS)的一个组件,它作为日志采集客户端,提供了一种高效且易于配置的方式来接入日志数据。由于Logtail是用C++语言编写的,它具有较高的性能和较低的资源消耗,适合在各种环境下运行,包括资源受限的环境。

Logtail的主要功能包括:
  1. 无侵入式采集:Logtail能够监视指定的日志文件,实时采集更新的日志数据,而无需修改现有的应用程序代码。
  2. 多种日志格式支持:除了采集标准的文本日志文件,Logtail还能够采集其他类型的日志,如MySQL的二进制日志(binlog)、HTTP数据、以及容器日志等。
  3. 容器化环境支持:Logtail提供对容器友好的支持,无论是标准容器还是处于Kubernetes集群中的容器,Logtail都能够灵活地采集日志数据。
  4. 异常处理:Logtail有一套完备的机制来处理在日志采集过程中可能出现的异常,如当遇到网络故障或服务端异常时,它可以通过主动重试或将数据缓存到本地等方式来确保数据的安全和完整性。

由于Logtail的这些特性,它成为了一个非常强大且可靠的日志采集工具,特别是在需要确保日志数据不丢失的情况下。它的无侵入性质意味着开发者或运维人员可以在不影响现有应用程序的情况下部署日志采集,而其对容器和微服务架构的友好支持则使其非常适合现代云原生应用。

Logtail作为日志采集客户端,其文件采集流程设计得非常全面,涵盖了从日志生成到传输的多个关键环节。以下是Logtail文件采集流程的六个主要步骤:
  1. 文件监听 :Logtail监控配置中指定的日志文件或目录,以便于检测文件的创建、更新或者删除事件。这通常通过文件系统的通知机制来实现,如Linux上的inotify。
  2. 文件读取 :一旦文件发生变化,Logtail会读取文件内容。对于更新的文件,Logtail保持追踪当前的读取进度,确保从上次停止的地方继续读取,避免重复或遗漏。
  3. 日志处理 :读取到的原始日志数据会经过初步处理,比如分割日志行、解析字段等,将原始文本转换为结构化的日志记录。
  4. 日志过滤 :用户可以定义过滤规则,根据日志内容或属性决定哪些日志记录应该被采集,哪些应该被忽略。这一步可以减少无用数据的传输和存储,优化成本和性能。
  5. 日志聚合 :在发送之前,Logtail可以对日志进行批处理,合并多个日志记录为一个数据包,减少网络请求的次数,提高数据传输效率。
  6. 数据发送 :聚合后的日志数据会被发送到配置的目标,例如阿里云日志服务SLS的服务器。数据发送过程中,Logtail会处理网络异常等问题,确保数据的可靠传输。



以上六个环节构成了Logtail文件采集的完整流程,每个步骤都是为了确保日志数据的准确、高效、可靠采集。在高可用性和高性能的要求下,Logtail的设计使得日志数据能够从源头到目的地的迁移过程中保持完整性和一致性。



对比开源的logstash,和logtail的对比如下:

功能项

logstash

logtail

日志读取

轮询

事件触发

文件轮转

支持

支持

Failover处理 (本地checkpoint)

支持

支持

通用日志解析

支持grok(基于正则表达式)解析

支持正则表达式解析

特定日志类型

支持delimiter、key-value、json等主流格式

支持key-value格式

数据发送压缩

插件支持

LZ4

数据过滤

支持

支持

数据buffer发送

插件支持

支持

发送异常处理

插件支持

支持

运行环境

JRuby实现,依赖JVM环境

C++实现,无特殊要求

线程支持

支持多线程

支持多线程

热升级

不支持

支持

中心化配置管理

不支持

支持

运行状态自检

不支持

支持cpu/内存阈值保


总体来说,logstash支持所有主流日志类型,插件支持最丰富,可以灵活DIY,但性能较差,JVM容易导致内存使用量高。logtail占用机器cpu、内存资源最少,结合阿里云日志服务的E2E体验良好,但目前对特定日志类型解析的支持较弱。


  • 加工与聚合


阿里云日志服务(SLS)提供的数据加工功能是一项强大的特性,使得用户能够在日志数据进入存储和分析阶段之前对其进行预处理。这些功能对于数据的清洗、转换、安全保护和增强都非常关键,尤其是在构建复杂的数据处理流水线时。以下是数据加工功能的几个关键点和它们的应用场景。

  1. 数据规整:这个功能允许用户对无结构或半结构化的日志进行字段提取和格式转换,从而得到结构化数据。这对于后续的实时处理、分析和数据仓库计算来说是非常重要的,因为结构化数据更容易进行查询和分析。

  2. 数据富化:数据富化功能可以通过字段连接(JOIN)操作,将日志数据(如订单日志)和其他维度数据(如用户信息表)相结合。这样可以为原始日志添加更丰富的上下文信息,提高数据分析的深度和准确性。

  3. 数据流转:流转功能支持跨地域的数据传输,允许用户将数据从一个地域转移到另一个地域。例如,可以将海外地域的日志数据传输到中心地域,实现日志的集中化管理和分析。

  4. 数据脱敏:数据脱敏对于符合隐私法规和公司政策是必不可少的。SLS可以在日志数据中识别并隐藏或替换敏感信息,比如密码、手机号或地址等,来保护用户隐私。

  5. 数据过滤:数据过滤允许用户基于特定条件选择性地提取日志条目。这可以用来隔离关键服务的日志,以便进行重点监控和分析。


应用场景:

  1. 数据规整(一对一):从单个源Logstore读取数据,经过加工处理后,输出到单个目标Logstore,适用于简单的加工任务。

  2. 数据分派(一对多):从单个源Logstore读取数据,根据不同的规则或条件将数据加工后分发到多个目标Logstore中,适用于需要将日志按类型或用途分开处理的场景。

  3. 数据融合(多对一):从多个源Logstore读取数据,将它们合并或加工后输出到一个目标Logstore中,适用于需要综合多个数据源进行统一分析的场景。


通过这些数据加工功能,SLS能够提供灵活、强大的数据处理能力,帮助用户更好地管理和分析日志数据,同时保护敏感信息。这对于需要实时数据洞察和快速响应的企业来说是非常有价值的。

  • 存储和查询


阿里云日志服务(SLS)的存储引擎专门为处理可观测数据而设计,包括日志、指标和追踪数据。它旨在提供一个统一和高效的解决方案,用以满足大规模数据存储和快速查询的需求。下面是对这个存储引擎的两个关键特性的进一步解读:

统一性(Unified)
  1. 多类型数据支持:统一的存储引擎意味着它能够存储和处理多种类型的可观测数据,无论是结构化的日志数据、时间序列的指标数据还是分布式追踪的信息。这样的设计减少了需要使用和管理多个存储系统的复杂性。
  2. 统一查询接口:有了统一的存储引擎,用户可以通过一个共同的查询接口访问不同类型的数据,这提高了使用效率,并允许跨数据类型的分析和关联。
  3. 统一的管理和运维:统一存储引擎也简化了数据的管理和运维任务,因为它减少了需要学习和监控的工具和接口的数量。

性能(Fast)
  1. 高速写入:SLS的存储引擎针对高速数据写入进行了优化,以支持大数据量的实时采集。这对于高流量网站、大型分布式应用和IoT设备等场合至关重要。
  2. 高效查询:SLS存储引擎还针对快速查询进行了优化,确保即使在涉及大规模数据集时,也能够快速返回结果。
  3. 适应大规模场景:阿里云天然服务于各种规模的企业,包括那些拥有超大规模数据场景的企业。SLS的存储引擎设计考虑了这些场景的需求,保证了扩展性和稳定性。

总体来说,SLS的存储引擎通过统一和快速的写入与查询功能,为处理大规模的可观测数据提供了一个强大、高效且易于管理的解决方案。整体架构如下:


接入层协议支持:

  1. 接入层的设计非常灵活,支持多种数据写入协议,这意味着无论数据来源是什么(如应用程序、服务器、网络设备等),都能够顺利与SLS集成。

  2. 写入的数据首先被放入一个先进先出(FIFO)的管道中,这个管道模型类似于Apache Kafka的消息队列(MQ)模型,提供了高吞吐量和可靠性。


管道和数据消费:

  1. 管道不仅存储数据,还支持数据消费,允许各类下游系统(如实时分析、告警、可视化工具等)接入并处理数据。

  2. 这种设计实现了数据的异步处理,增强了系统的整体扩展性和弹性。


索引结构和存储优化:

  1. 在管道之上,存储引擎为不同类型的数据(Traces/Logs和Metrics)构建了两套索引结构。

  2. 利用倒排索引(Inverted Index)、列式存储(Columnar Storage)以及压缩(Compaction)等技术,SLS能够提供快速的查询性能,同时优化存储空间的使用效率。


统一的进程架构:

  1. 上述所有功能都在一个统一的进程内实现,这意味着可以在单一的系统中处理日志、指标和追踪数据,大大简化了运维和部署的复杂性和成本。


纯分布式框架:

  1. SLS的存储引擎基于纯分布式架构构建,支持水平扩展,可以随着数据量的增长而增加更多的存储节点。

  2. 单个存储节点(Store)能够处理极高的数据量,最多支持每天处理PB级别的数据写入,适合大规模的数据中心和云环境。


在查询方面,SLS的查询和分析引擎整合了多种查询语法和分析能力,提供了一个强大的查询环境:

  1. 关键词查询:支持对存储在SLS中的日志数据进行全文搜索,类似于其他日志管理系统中的关键词查询功能。这主要用于快速查找特定日志条目或筛选日志信息。

  2. PromQL的语法兼容:PromQL是Prometheus查询语言,专门用于时间序列数据的查询。SLS兼容PromQL语法,使得用户能够利用PromQL进行指标数据的查询和分析。

  3. SQL作为顶层分析语言:1、为了实现对可观测数据的深入分析,SLS扩展了SQL语言的能力,使其能够支持连接关键词查询、PromQL等语法;2、通过SQL,用户可以执行更复杂的数据操作,如聚合、排序、联接和子查询等,这为数据分析提供了极大的灵活性。

  4. 外部数据源和机器学习模型的集成:SLS的SQL扩展还允许用户连接外部数据库和机器学习模型。这意味着用户可以将SLS中的数据与其他数据源相结合,或使用机器学习算法进行高级分析。

  5. 可观测数据的融合分析:SLS使得日志、指标和追踪数据能够在一个统一的分析环境中进行关联和分析。这种融合分析能力使用户能够以多维度来理解和分析系统的行为和性能。


通过这样的设计,SLS提供了一个功能全面的查询和分析环境,可以满足从简单的文本搜索到复杂的数据挖掘和分析的各种需求。对于开发人员、运维人员和数据分析师来说,SLS的查询功能极大地提高了他们处理和分析可观测数据的效率和准确性。



  ELK 对比 SLS


ELK的核心 ElasticSearch 基于 Lucene 全文索引实现,面向 Document 类型,构建一套完整的可观测分析平台需要组合多款服务,这其中包括 logstash、Kibana、Kafka、Flink、TSDB、Prometheus 等服务,对于规模、查询能力等方面存在一定限制,整体成本较高。

日志服务 SLS 基于阿里云自研灵活索引,支持一站式云原生可观测性,多规格多场景支持。

详细对比下两者在以下方面的差异:
  1. 性能:在同样成本的介质上部署,SLS 能够提供更低的延时、以及更稳定的查询性能。
  2. 成本:这里的成本不光是部署成本,还需要包括使用成本。要维护良好状态的 ELK 集群,需要从容量规划、稳定性保障、性能调优、数据高可用,数据如何在不同系统间关联等多个方面下功夫。SLS 全托管免运维无需花费额外人力投入。百 TB 规模下,SLS 综合成本是 ELK 的 44%。
  3. 易用性:SLS 对开源协议及组件相比 ELK 有更好的兼容性。利用 ELK 构建完整可观测分析平台需组合多款服务,这其中包括Logstash、Kibana、Kafka、Flink、TSDB、Prometheus等。SLS 在这个场景上无需多个组件搭配,支持一站式数据监控分析平台能力。
  4. 互联互通与二次开发:在可观测场景中,我们需要跨多个数据源进行关联分析SLS 可观测数据统一存储支持 log metric trace 数据存储,打通数据孤岛,并且提供开源友好的 API 接口进行使用。
  5. 附加能力(新算子、告警等):开源 ELK 仅支持指标分析聚合、分桶聚合、管道分析、矩阵分析有限的聚合算法。SLS 针对数据分析场景支持 30+ 聚合计算函数,丰富的机器学习函数以及多渠道数据源,是 ELK 提供操作符的 5 倍,充分发掘数据分析能力。除此之外,SLS 内置告警功能可以快速开箱即用,无需搭建系统。

参考文章

  1.  https://icyfenix.cn/distribution/observability/tracing.html 
  2.  https://tech.meituan.com/2023/04/20/traceid-google-dapper-mtrace.html 
  3.  https://tech.meituan.com/2022/07/21/visualized-log-tracing.html 
  4.  https://developer.aliyun.com/article/1065820#slide-4 
  5.  https://aws.amazon.com/cn/what-is/elk-stack/

团队介绍


我们来自淘天集团的营销与平台策略技术。我们支撑大促 (双11、618 等)和日销业务;同时我们也直面竞对,深入参与淘宝好价、百亿补贴、聚划算等日销业务的价格心智打造。秉承“简单、开放、自驱”的技术文化,在使命与责任中互相成就,共同成长,践行让业务先赢,技术氛国浓郁。


¤  拓展阅读  ¤

3DXR技术 |  终端技术 |  音视频技术

服务端技术 | 技术质量 | 数据算法



本文分享自微信公众号 - 大淘宝技术(AlibabaMTT)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
15 收藏
0
分享
返回顶部
顶部