分布式架构的观测

分布式架构的观测

在一个分布式应用中,如果出现了某个异常,那我们必然不可能只依靠 awk、grep 等命令来查看日志分析问题,往往分布式架构的一个异常都贯通多个节点,我们需要将多个节点联系起来排查问题。这就引出了分布式架构的可观测性,可观测性越高,排查问题越轻松

学术界一般会将可观测性分解为三个更具体方向进行研究,分别是:事件日志、链路追踪和聚合度量,这三个方向各有侧重,又不是完全独立

日志

日志的职责是记录离散事件,通过这些记录事后分析出程序的行为,譬如曾经调用过什么方法,曾经操作过哪些数据

只要稍微复杂点的系统,尤其是复杂的分布式系统,往往还要有专门的全局查询和可视化功能。此时,从打印日志到分析查询之间,还隔着收集、缓冲、聚合、加工、索引、存储等若干个步骤。我们简单的梳理一下日志存储与打印的流程,下面所说的流程,就是 ELK 技术栈

日志的输出

日志输入就是我们在代码中使用 log 提供的方法输出,当然 print 等方法也可以,不过不推荐。这块主要是开发的工作,打出优秀的日志可以便于排查问题,我们应该尽可能的避免如下几点:

  • 日志不能太多,也不能太少,不必把上下文的所有消息都打进去,否则会造成 IO 问题。有些不必要的 info 日志,在测试的时候可以打出来用于排查问题,但是项目上线后就需要删掉
  • 避免打印敏感信息。不用专门去提醒,任何程序员肯定都知道不该将密码,银行账号,身份证件这些敏感信息打到日志里
  • 避免引用慢操作。日志中打印的信息应该是上下文中可以直接取到的,如果当前上下文中根本没有这项数据,需要专门调用远程服务或者从数据库获取,又或者通过大量计算才能取到的话,那应该先考虑这项信息放到日志中是不是必要且恰当的

我们应当尽可能做到如下几点:

  • 处理请求时的 TraceID
  • 系统运行过程中的关键事件。日志的职责就是记录事件,进行了哪些操作、发生了与预期不符的情况、运行期间出现未能处理的异常或警告、定期自动执行的任务,等等,都应该在日志中完整记录下来
  • 启动时输出配置信息

收集与缓冲

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

为了处理多个节点的协调,除了今天流行的日志收集器,轻量高效的 Filebeat 以外,还需要有专门用于其他功能的组件,比如用于审计数据的 Auditbeat、用于无服务计算架构的 Functionbeat、用于心跳检测的 Heartbeat 等等

至于缓冲,是指流量大的时候,会有过多的日志打进 DB,导致数据库压力过大。一种最常用的缓解压力的做法是将日志接收者从 Logstash 和 Elasticsearch 转移至抗压能力更强的队列缓存,譬如在 Logstash 之前架设一个 Kafka 或者 Redis 作为缓冲层

加工与聚合

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

为了加快查询速度,我们必须要建立索引,但是对一串巨大的字符串直接建立索引肯定不是优秀的解法,我们可以根据输出日志的特征、形式,来对字符串做拆分,拆出来的数据就可以建立不同的索引了

这就引出了我们处理加工聚会的工具 Logstash 。Logstash 的基本职能是把日志行中的非结构化数据,通过 Grok 表达式语法转换为上面表格那样的结构化数据,然后以 JSON 格式输出到 Elasticsearch 中(这是最普遍的输出形式,Logstash 输出也有很多插件可以具体定制不同的格式)

经过 Logstash 转换,已经结构化的日志,Elasticsearch 便可针对不同的数据项来建立索引,进行条件查询、统计、聚合等操作的了

存储与查询

在经过一系列的数据处理后,我们终于可以把数据放进 DB 了,也就是 ES Elasticsearch

Elasticsearch 只提供了 API 层面的查询能力,它通常搭配同样出自 Elastic.co 公司的 Kibana 一起使用,可以将 Kibana 视为 Elastic Stack 的 GUI 部分

Kibana 尽管只负责图形界面和展示,但它提供的能力远不止让你能在界面上执行 Elasticsearch 的查询那么简单。Kibana 宣传的核心能力是"探索数据并可视化",即把存储在 Elasticsearch 中的数据被检索、聚合、统计后,定制形成各种图形、表格、指标、统计,以此观察系统的运行状态,找出日志事件中潜藏的规律和隐患

追踪

微服务时代,追踪就不只局限于调用栈了,一个外部请求需要内部若干服务的联动响应,这时候完整的调用轨迹将跨越多个服务,同时包括服务间的网络传输信息与各个服务内部的调用堆栈信息。追踪的主要目的是排查故障,如分析调用链的哪一部分、哪个方法出现错误或阻塞,接口的输入输出是否符合预期

Dapper 提出了追踪与跨度两个概念。从客户端发起请求抵达系统的边界开始,记录请求流经的每一个服务,直到到向客户端返回响应为止,这整个过程就称为一次追踪 (Trace)。由于每次 Trace 都可能会调用数量不定、坐标不定的多个服务,为了能够记录具体调用了哪些服务,以及调用的顺序、开始时点、执行时长等信息,每次开始调用服务前都要先埋入一个调用记录,这个记录称为一个跨度

数据收集

追踪数据一般可以使用以下三种方式来实现

  • 基于日志的追踪:我们将 TrackID 打进日志里,然后随着所有节点的日志归集过程汇聚到一起,再从全局日志信息中反推出完整的调用链拓扑关系。打入日志这个操作完全可以由插件实现,做到对开发透明,但是缺点是由于日志归集不及时或者精度丢失,导致日志出现延迟或缺失记录的话,会进而产生追踪失真
  • 基于服务的追踪:这是目前最主流的追踪方式,服务追踪的实现思路是通过某些手段给目标应用注入追踪探针(Probe),探针在结构上可视为一个寄生在目标服务身上的小型微服务系统,把从目标系统中监控得到的服务调用信息,通过另一次独立的 HTTP 或者 RPC 请求发送给追踪系统
  • 基于边车代理的追踪:我们知道边车模式是对服务加一层代理,通过代理来对外层进行交互。边车代理是服务网格的专属方案,也是最理想的分布式追踪模型。它对应用完全透明,无论是日志还是服务本身都不会有任何变化;它与程序语言无关,无论应用采用什么编程语言实现,只要它还是通过网络来访问服务就可以被追踪到

度量

度量是指对系统中某一类信息的统计聚合。譬如,证券市场的每一只股票都会定期公布财务报表,通过财报上的营收、净利、毛利、资产、负债等等一系列数据来体现过去一个财务周期中公司的经营状况,这便是一种信息聚合。像是任务管理器,就是度量的一种

度量(Metrics)的目的是揭示系统的总体运行状态,因此可以拆分为监控(Monitoring)和预警(Alert),如某些度量指标达到风险阈值时触发事件,以便自动处理或者提醒管理员介入

指标收集部分要解决两个问题:如何定义指标 以及如何将这些指标告诉服务端, 如何定义指标这个问题听起来应该是与目标系统密切相关的,必须根据实际情况才能讨论,其实并不绝对,无论目标是何种系统,都是具备一些共性特征。确定目标系统前我们无法决定要收集什么指标,但指标的数据类型(Metrics Types)是可数的,所有通用的度量系统都是面向指标的数据类型来设计的:

  • 计数度量器(Counter):这是最好理解也是最常用的指标形式,计数器就是对有相同量纲、可加减数值的合计量,譬如业务指标像销售额、货物库存量、职工人数等等;技术指标像服务调用次数、网站访问人数等都属于计数器指标
  • 吞吐率度量器(Meter):吞吐率度量器顾名思义是用于统计单位时间的吞吐量,即单位时间内某个事件的发生次数。譬如交易系统中常以 TPS 衡量事务吞吐率,即每秒发生了多少笔事务交易
  • 时间度量器(Timer):用来统计一个方法或者一个接口的执行时间,然后我们收集 P98 或者 P95 的信息来进行预警,对接口优化

而如何将这些指标告诉服务端这个问题,通常有两种解决方案:拉取式采集推送式采集,所谓 Pull 是指度量系统主动从目标系统中拉取指标,相对地,Push 就是由目标系统主动向度量系统推送指标。不管是拉取还是推送,在进行操作之前,指标数据会记在内存中

存放指标一般不会选择 MySQL 或者 PG 等关系型数据库,而是时序数据库。时序数据库用于存储跟随时间而变化的数据,并且以时间(时间点或者时间区间)来建立索引的数据库

时间序列数据是历史烙印,具有不变性,、唯一性、有序性。时序数据库同时具有数据结构简单,数据量大的特点

时序数据通常只是追加,很少删改或者根本不允许删改。针对数据热点只集中在近期数据、多写少读、几乎不删改、数据只顺序追加这些特点,时序数据库被允许做出很激进的存储、访问和保留策略:

  • 以日志结构的合并树代替传统关系型数据库中的 B+Tree 作为存储结构,LSM 适合的应用场景就是写多读少,且几乎不删改的数据
  • 设置激进的数据保留策略,譬如根据过期时间自动删除相关数据以节省存储空间,同时提高查询性能。对于普通数据库来说,数据会存储一段时间后就会被自动删除这种事情是不可想象的
  • 对数据进行再采样以节省空间,譬如最近几天的数据可能需要精确到秒,而查询一个月前的数据时,只需要精确到天,查询一年前的数据时,只要精确到周就够了,这样将数据重新采样汇总就可以极大节省存储空间
相关推荐
小马爱打代码29 分钟前
SpringCloud(注册中心+OpenFeign+网关+配置中心+服务保护+分布式事务)
分布式·spring·spring cloud
BUTCHER52 小时前
Kafka安装篇
分布式·kafka
04Koi.5 小时前
Java项目--仿RabbitMQ的消息队列--虚拟主机设计
分布式·rabbitmq
若雨叶6 小时前
Kafka实现监听多个topic
分布式·kafka
HaoHao_0107 小时前
云消息队列 Kafka 版
分布式·阿里云·kafka·云计算·云服务器
中东大鹅8 小时前
分布式数据存储基础与HDFS操作实践
大数据·linux·hadoop·分布式·hbase
04Koi.10 小时前
Java项目--仿RabbitMQ的消息队列--网络通信协议设计
分布式·rabbitmq
Code apprenticeship11 小时前
RabbitMQ如何实现延时队列?
分布式·rabbitmq
龙哥·三年风水12 小时前
workman服务端开发模式-应用开发-后端api推送工具开发
分布式·gateway·php
爱学测试的雨果12 小时前
分布式测试插件 pytest-xdist 使用详解
分布式·pytest