微服务已成为全新应用程序的默认选择。毕竟,从业者认为,微服务提供了完全数字化转型所需的解耦类型,使各个团队能够以比以往任何时候都更快的速度进行创新。
微服务只不过是更大规模的常规分布式系统。因此,它们加剧了任何分布式系统都面临的众所周知的问题,例如在跨流程边界的业务事务中缺乏可见性。
考虑到在生产环境中同时运行单个服务的多个版本非常常见——无论是在 A/B 测试 环境中,还是作为遵循 金丝雀发布 技术的 新版本发布的一部分——当我们考虑到我们正在谈论数百个服务时,很明显我们所拥有的是混乱。几乎不可能映射服务及其版本之间的相互依赖关系,并了解业务事务的路径。
只要我们能够观察正在发生的事情并诊断最终将出现的问题,这种混乱最终会是一件好事。
可观测性
当我们可以根据系统发出的 指标、日志和追踪 来理解其状态时,就称该系统是可观测的。鉴于我们正在谈论分布式系统,仅仅了解单个服务的单个实例的状态是不够的;我们需要能够聚合给定服务的所有实例的指标,可能按版本分组。像 Prometheus 这样的指标解决方案在解决可观测性问题的这一方面非常受欢迎。同样,我们需要将日志存储在中心位置,因为不可能分析每个服务各个实例的日志。Logstash 通常应用于此处,并结合像 Elasticsearch 这样的后端存储。最后,我们需要获得端到端追踪,以了解给定事务采取的路径。这就是分布式追踪解决方案发挥作用的地方。
分布式追踪
在单体 Web 应用程序中,日志框架提供了足够的功能来在发生故障时进行基本的根本原因分析。开发人员只需要在代码中放置日志语句即可。诸如“上下文”(通常是“线程”)和“时间戳”之类的信息会自动添加到日志条目中,从而更容易理解给定请求的执行并关联条目。
Thread-1 2018-09-03T15:52:54+02:00 Request started
Thread-2 2018-09-03T15:52:55+02:00 Charging credit card x321
Thread-1 2018-09-03T15:52:55+02:00 Order submitted
Thread-1 2018-09-03T15:52:56+02:00 Charging credit card x123
Thread-1 2018-09-03T15:52:57+02:00 Changing order status
Thread-1 2018-09-03T15:52:58+02:00 Dispatching event to inventory
Thread-1 2018-09-03T15:52:59+02:00 Request finished
我们可以安全地说,上面的第二个日志条目与其他条目不相关,因为它是在不同的线程中执行的。
在微服务架构中,仅靠日志记录无法提供完整的画面。这个服务是调用链中的第一个吗?库存服务(我们显然在那里分派了一个事件)发生了什么?
回答这个问题的一个常见策略是在事务的第一个构建块中创建一个标识符,并在所有调用中传播该标识符,可能会在进行远程调用时将其作为 HTTP 标头发送。
在中央日志收集器中,我们可以看到如下所示的条目。请注意,我们如何记录关联 ID(示例中的第一列),因此我们知道第二个条目与其他条目不相关。
abc123 Order 2018-09-03T15:52:58+02:00 Dispatching event to inventory
def456 Order 2018-09-03T15:52:58+02:00 Dispatching event to inventory
abc123 Inventory 2018-09-03T15:52:59+02:00 Received `order-submitted` event
abc123 Inventory 2018-09-03T15:53:00+02:00 Checking inventory status
abc123 Inventory 2018-09-03T15:53:01+02:00 Updating inventory
abc123 Inventory 2018-09-03T15:53:02+02:00 Preparing order manifest
这项技术是任何现代分布式追踪解决方案核心概念之一,但它并不是真正的新事物;关联日志条目已有数十年的历史,可能与“分布式系统”本身一样古老。
分布式追踪与常规日志记录的不同之处在于,保存追踪数据的数据结构更加专业化,因此我们还可以识别因果关系。查看上面的日志条目,很难判断最后一步是否由前一个条目引起,它们是同时执行的,还是它们共享同一个调用者。拥有专用的数据结构还允许分布式追踪不仅记录时间点上的消息,还记录给定过程的开始和结束时间。

显示类似于上述日志的 Span 追踪
大多数现代分布式追踪工具都受到 2010 年关于 Dapper 的论文的启发,Dapper 是 Google 使用的分布式追踪解决方案。在那篇论文中,上面描述的数据结构被称为 Span,您可以在上图中看到九个 Span。这种特殊的 Span “森林”被称为追踪,相当于我们之前看到的关联日志条目。
上图是在 Jaeger 中显示的追踪截图,Jaeger 是由 云原生计算基金会 (CNCF) 托管的开源分布式追踪解决方案。它用颜色标记每个服务,以便更容易看到流程边界。时间信息可以很容易地可视化,既可以通过查看屏幕顶部的宏观时间线,也可以查看单个 Span,从而了解每个 Span 花费的时间以及它在特定执行中的影响。也很容易观察到进程何时是异步的,因此可能会比初始请求存活更长时间。
与日志记录一样,我们需要使用我们要记录的数据来注释或检测我们的代码。与日志记录不同,我们记录 Span 而不是消息,并进行一些划界以了解 Span 何时开始和结束,以便我们可以获得准确的时间信息。由于我们可能希望我们的业务代码独立于特定的分布式追踪实现,因此我们可以使用诸如 OpenTracing 之类的 API,将关于具体实现的决策留作打包或运行时问题。以下是显示此类划界的伪 Java 代码。
try (Scope scope = tracer.buildSpan("submitOrder").startActive(true)) {
scope.span().setTag("order-id", "c85b7644b6b5");
chargeCreditCard();
changeOrderStatus();
dispatchEventToInventory();
}
鉴于分布式追踪概念的性质,很明显在我们的业务服务“之间”执行的代码也可以成为追踪的一部分。例如,我们可以为 Istio 启用分布式追踪集成,开启 Istio 是一个服务网格解决方案,可帮助微服务之间的通信,我们将突然对网络延迟和在此层做出的路由决策有更好的了解。另一个例子是 OpenTracing 社区在为流行的堆栈、框架和 API 提供检测方面所做的工作,例如 Java 的 JAX-RS、Spring Cloud 或 JDBC。这使我们能够了解我们的业务代码如何与中间件的其余部分交互,了解潜在问题可能发生在哪里,并确定改进的最佳领域。事实上,今天的中间件检测非常丰富,以至于通常仅使用所谓的“框架检测”即可开始使用分布式追踪,而使业务代码免于任何与追踪相关的代码。
虽然对于老牌公司更快地创新和雄心勃勃的初创公司实现 Web 规模而言,微服务架构在当今几乎是不可避免的,但是当最终发生故障且没有合适的工具可用时,很容易在进行根本原因分析时感到无助。好消息是,像 Prometheus、Logstash、OpenTracing 和 Jaeger 这样的工具提供了将可观测性带入您的应用程序的组件。
Juraci Paixão Kröhling 将在 10 月 22 日至 24 日于苏格兰爱丁堡举行的 欧洲开源峰会 上展示 我的微服务在做什么?。
评论已关闭。