SpringBoot3 WebFlux 可观测最佳实践

前言

链路追踪是可观测性软件系统的一个非常好的工具。它使开发人员能够了解应用程序中和应用程序之间不同交互发生的时间、地点和方式。同时让观测复杂的软件系统变得更加容易。

Spring Boot 3开始,Spring Boot 中用于链路追踪的旧 Spring Cloud Sleuth 解决方案将替换为新的 Micrometer Tracing 库。

您可能已经了解 Micrometer,因为它以前被用作公开独立于平台的指标和监控基于 JVM 的微服务(例如 Prometheus )的默认解决方案。最新产品通过独立于平台的链路追踪解决方案扩展了 Micrometer 生态系统。这使得开发人员能够使用一个通用 API 来检测其应用程序,并以不同的格式将其导出到 Jaeger、Zipkin 或 OpenTelemetry 等链路追踪收集器。

本文将介绍在响应式编程 Kotlin 中,如何在 Spring Boot 3 WebFlux 利用 Micrometer 进行链路追踪。

1. 微服务设置

接下来,我们将创建一个简单的 Spring Boot 微服务,它提供一个响应式 REST 端点,该端点在内部查询另一个第三方服务以获取一些信息。目标是导出两个操作的 trace。

我们将从以下 Spring Boot Initializr 项目开始,您可以在此处找到该项目。它包括带有Kotlin Gradle DSLSpring Boot 3.0.1Spring Web Reactive (WebFlux)和带有PrometheusSpring Actuator。以下代码主要使用Kotlin,但如果使用 Java 也是可以的,大多数方法都是相同的。

Spring 初始化模板 带有 Webflux、Spring Actuator 和 Prometheus 的 Spring Boot 3 Kotlin 模板

定义 endpoint

我们将首先添加一个带有测试 endpoint 的简单 REST 控制器类,该测试 endpoint 使用 Spring WebClient 调用外部 API 。我们正在使用 suspend 关键字来使用Kotlin的协程。这使我们能够在利用 Spring WebFlux 的响应式流的同时编写命令式代码。

在以下示例中,我们使用 Spring WebClient 调用外部 TODO-API,该 API 以 JSON 字符串形式返回 TODO 项。我们还将创建一条日志消息,其中稍后应包含一些链路追踪信息。

@RestController
class Controller {val log = LoggerFactory.getLogger(javaClass)val webClient = WebClient.builder().baseUrl("https://jsonplaceholder.typicode.com").build()@GetMapping("/test")suspend fun test(): String {// simulate some complex calculation  delay(1.seconds)log.info("test log with tracing info")// make web client call to external APIval externalTodos = webClient.get().uri("/todos/1").retrieve().bodyToMono(String::class.java).awaitSingle()return externalTodos}
}

新增 Micrometer tracing

在下一步中,我们将把 Micrometer tracing 依赖项添加到我们的build.gradle.kts文件中。由于 Micrometer 支持不同的链路追踪格式和供应商,因此依赖项被分开,我们只导入我们需要的内容。为了保持所有依赖项同步,我们使用 Micrometer Tracing BOM(bom 清单)。此外,我们添加了核心依赖项和桥接器,以将 Micrometer Tracing 转换为 OpenTelemetry 格式(其他格式也可用)。

implementation(platform("io.micrometer:micrometer-tracing-bom:1.0.0"))
implementation("io.micrometer:micrometer-tracing")
implementation("io.micrometer:micrometer-tracing-bridge-otel")

我们还需要添加导出器依赖项来导出创建的 trace。在此示例中,我们将使用由 OpenTelemetry 维护并由 Micrometer Tracing 支持的 Zipkin 导出器。

implementation("io.opentelemetry:opentelemetry-exporter-zipkin")

配置

配置是设置链路追踪必不可少的一步,配置文件application.yaml位于src/main/resources目录下。

  • 首先,我们必须在管理设置中启用链路追踪。我们还将链路追踪采样率设置为 1(默认值为 0.1),以便为服务收到的每个调用创建链路追踪。在具有大量请求的生产系统中,您可能只想追踪一些调用链路。
  • 此外,我们可以定义希望 Zipkin 导出器发送链路追踪的端点 URL。
  • 最后,我们必须更新默认日志记录模式以包含链路追踪和 spanId。
management:tracing:enabled: truesampling.probability: 1.0zipkin.tracing.endpoint: http://localhost:9411/api/v2/spanslogging.pattern.level: "trace_id=%mdc{traceId} span_id=%mdc{spanId} trace_flags=%mdc{traceFlags} %p"

2. 测试

现在我们已经完成了服务设置,我们可以运行它了。如果启动应用程序,默认情况下,服务器应在端口下启动 8080。然后,可以通过打开浏览器来调用我们创建的端点http://localhost:8080/test。以下是请求响应内容:

{  "userId" :  1 ,  "id" :  1 ,  "title" :  "delectus aut autem" ,  "已完成" :  false  }

要查看调用端点时创建的实际链路追踪,我们需要收集并查看它们。在本教程中,我们将使用zipkin导出器将数据导出到 观测云。当然也可以使用其他系统,例如 Zipkin、Grafana Loki 或 Datadog。

现在您可以再次调用我们的 Spring Boot 服务的端点。之后,当您在 观测云 中搜索任何 tracing 时,您应该能够找到端点请求的链路追踪信息。

3. 问题

乍一看,一切似乎都运行良好。然而,我们有两个问题。

解决了部分 issue 问题,这些问题可以在 Micrometer Tracing 文档中找到。

日志缺少数据

如果我们查看应用程序日志,可以发现调用端点时发出的日志消息。

trace_id= span_id= trace_flags= INFO 43636 --- [DefaultExecutor] com.example.tracing.Controller           : test log with tracing info

正如你所看到的,trace_id 和 span_id没有设置。这是因为Micrometer Tracing还无法轻松处理响应式流中的链路追踪上下文。此外,响应式流的Kotlin协程包装器隐藏了链路追踪上下文。因此,我们必须推迟当前响应式流的上下文来获取链路追踪信息。实际上,这看起来如下所示:

 Mono.deferContextual { contextView ->ContextSnapshot.setThreadLocalsFrom(contextView,ObservationThreadLocalAccessor.KEY).use {log.info("test log with tracing info")Mono.empty<String>()}
}.awaitSingleOrNull()

为了更符合应用性,我们可以将示例代码提取到一个单独的函数中。

@GetMapping("/test")
suspend fun test(): String {// ...observeCtx { log.info("test log with tracing info") }// ...
}suspend inline fun observeCtx(crossinline f: () -> Unit) {Mono.deferContextual { contextView ->ContextSnapshot.setThreadLocalsFrom(contextView,ObservationThreadLocalAccessor.KEY).use {f()Mono.empty<Unit>()}}.awaitSingleOrNull()
}

如果我们现在启动应用程序并调用我们的端点,我们应该能够trace_id在日志中看到。

trace_id=6c0053eba01199f194f5f76ff8d61917 span_id=967d591266756905 trace_flags= INFO 45139 --- [DefaultExecutor] com.example.tracing.Controller           : test log with tracing info

WebClient 调用没有产生追踪信息

第二个问题可以通过查看观测云中的 trace 来发现。它仅显示端点的父链路追踪,但不显示调用的子范围 WebClient。理论上,Spring WebClient 以及 RestTemplate 都是由 Micrometer 自动检测的。但是如果我们查看代码,就会发现我们正在使用静态构建器方法 WebClient。为了从 WebClient 获取自动链路追踪,我们需要使用 Spring 框架提供的构建器 bean。它可以通过我们类的构造函数注入Controller。

@RestController
class Controller(webClientBuilder: WebClient.Builder
) {val webClient = webClientBuilder // use injected builder.baseUrl("https://jsonplaceholder.typicode.com").build()// ...}

通过上面的代码调整后重新调用 endpoint,我们在观测云中可以看到WebClient的跨度。Micrometer Tracing 还将自动为包含trace_id. 例如,如果我们调用另一个带有链路追踪功能的微服务,它可以获取 ID 并向观测云发送附加信息。

4. 观测指南

Micrometer Tracing 在 Spring 中自动为我们做了很多事情。但是,有时我们可能希望向链路追踪范围添加特定信息或观察应用程序中非传入或传出调用的特定部分。

添加跨度标签

我们可以定义自定义标签并将其添加到当前观察中以增强链路追踪数据。要检索当前链路追踪,我们可以使用ObservationRegistry类的 bean 。与日志记录问题类似,我们必须使用包装函数来获取正确的上下文。

@GetMapping("/test")
suspend fun test(): String {observeCtx {val currentObservation = observationRegistry.currentObservationcurrentObservation?.highCardinalityKeyValue("test_key", "test sample value")}// ...
}

添加此代码后,我们可以在观测云中看到我们的自定义标签及其值。

自定义可观测

使用 Micrometer API 创建自定义可观测(跨度)通常很容易。但是,在使用响应式流和协程时,我们需要帮助上下文链路追踪。如果我们在端点处理程序中创建一个新的观测,它将被视为一个单独的链路追踪。为了使代码可重用,我们可以编写一个简单的包装函数来创建新的观测点。它的工作原理与我们之前创建的用于使用 trace_id 。

suspend fun runObserved(name: String, observationRegistry: ObservationRegistry,f: suspend () -> Unit
) {Mono.deferContextual { contextView ->ContextSnapshot.setThreadLocalsFrom(contextView,ObservationThreadLocalAccessor.KEY).use {val observation = Observation.start(name, observationRegistry)Mono.just(observation).flatMap {mono { f() }}.doOnError {observation.error(it)observation.stop()}.doOnSuccess {observation.stop()}}}.awaitSingleOrNull()
}

该函数可以将任何挂起函数包装在新的观察周围。一旦执行了给定的函数,它将自动停止观测。此外,我们将追踪可能发生的任何错误并将其附加到链路追踪中。

我们现在可以应用这个函数来观察任何代码,例如函数的执行delay

@GetMapping("/test")
suspend fun test(): String {runObserved("delay", observationRegistry) {delay(1.seconds)}// ....
}

将此代码添加到端点处理程序后,观测云将向我们显示该操作的自定义范围。

5. 数据库链路追踪

典型的 Spring Boot 应用程序通常会连接到实际应用程序中的数据库。要利用响应式技术栈,建议使用 R2DBC API 而不是 JDBC 。

由于Micrometer Tracing是一项相当新的技术,目前还没有可用的自动追踪。然而,Spring 团队正在研究创建自动配置。实验存储库可以在这里找到。

当前项目,需将添加以下依赖项到build.gradle.kts. 为了方便测试,我们不会使用真实的数据库,而是使用 H2 内存数据库。

 implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")runtimeOnly("com.h2database:h2")runtimeOnly("io.r2dbc:r2dbc-h2")// R2DBC micrometer auto tracingimplementation("org.springframework.experimental:r2dbc-micrometer-spring-boot:1.0.2")

Kotlin代码中,添加了一个带有协程支持的简单 CRUD 存储库。如下所示:

@Table("todo")
data class ToDo(@Idval id: Long = 0,val title: String,
)interface ToDoRepository : CoroutineCrudRepository<ToDo, Long>@RestController
class Controller(val todoRepo: ToDoRepository,// ...
) {@GetMapping("/test")suspend fun test(): String {// ...// saveval entry = ToDo(0,"Springboot3 + WebFlux + Kotlin ")todoRepo.save(entry)// Sample traced DB callval dbtodos = todoRepo.findAll().toList()// ...return "${dbtodos.size} $externalTodos"}
}

调用我们的 endpoint 将会再添加一个跨度。新的跨度名为query,包含多个标签,包括Spring Data R2DBC 执行的 SQL 查询。

结论

Micrometer 和新的链路追踪扩展统一了Spring Boot 3及以上版本的可观测性技术栈。为不同公司及其技术栈使用的不同链路追踪解决方案提供了很好的抽象。因此,它简化了我们开发人员的工作。

在 Spring WebFlux 的响应式编程方面,仍然有一些改进的潜力,尤其是 Kotlin。Micrometer 团队正在与Project Reactor (Spring WebFlux 使用的响应式库)背后的团队进行积极会谈,以简化响应式技术栈的 Micrometer Tracing 的使用。

参考资源

kotlin-spring-boot-tracing-example

micrometer-metrics

micrometer tracing

r2dbc-micrometer-spring-boot

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/619510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity2022.3打包Android后从AB包加载场景发现丢失大量脚本问题

问题 这两天遇到一个问题&#xff0c;在VR项目打包Android的时候&#xff0c;加载场景后&#xff0c;Timeline工作不正常&#xff0c;找不到原因。 现象 看到有很多警告&#xff0c;丢失脚本的Log。 因为场景本身也有一些丢失的脚本所以没在意&#xff0c;但是又不是所有脚本…

计算机毕业设计-----SpringBoot招聘网站项目

项目介绍 SpringBoot招聘网站项目。主要功能说明&#xff1a; 管理员登录,简历管理,问答管理,职位管理,用户管理,职位申请进度更新,查看简历等功能。 用户角色包含以下功能&#xff1a;用户首页,登录注册,职位查看,职位详情,投递简历,查看我的申请,管理个人简历,附件简历管理…

紫光展锐M6780丨画质增强——更炫的视觉体验

智能显示被认为是推动数字化转型和创新的重要技术之一。研究机构数据显示&#xff0c;预计到2035年底&#xff0c;全球智能显示市场规模将达到1368.6亿美元&#xff0c;2023-2035年符合年增长率为36.4%。 随着消费者对高品质视觉体验的需求不断增加&#xff0c;智能手机、平板…

如何用ChatGPT写教案设计?以“沁园春雪”为例

1. 引言 随着人工智能技术的飞速发展&#xff0c;ChatGPT已成为教育领域的一大创新工具。ChatGPT不仅能够模拟人类对话&#xff0c;还可以帮助设计互动丰富、内容丰富的教案。本文将探索如何利用ChatGPT进行教案教学设计&#xff0c;特别是通过“沁园春雪”这一案例&#xff0…

基于反卷积方法的重大突破:结构光系统中的测量误差降低3倍

作者&#xff1a;小柠檬 | 来源&#xff1a;3DCV 在公众号「3DCV」后台&#xff0c;回复「原论文」可获取论文pdf 结构光三维测量技术在工业自动化、逆向工程和图形学领域越来越受欢迎。然而&#xff0c;现有的测量系统在成像过程中存在不完美&#xff0c;会导致在不连续边缘周…

【学习心得】Git深入学习

一、深入学习Git必须熟悉两个概念 &#xff08;1&#xff09;【四个区】Git本地有三个区&#xff0c;远程仓库也可以看出成一个区域 工作区、暂存区、本地仓库、远程仓库。 通过四句话来充分理解这三个区 第一句话&#xff1a;你创建的一个文件夹&#xff0c;并且将它初始化…

基于Java SSM框架实现体育竞赛成绩管理系统项目【项目源码+论文说明】

基于java的SSM框架实现体育竞赛成绩管理系统演示 摘要 体育竞赛是各种体育体育项目比赛的总称。是在裁判员的主持下&#xff0c;按统一的规则要求&#xff0c;组织与实施的体育员个体或体育队之间的竞技较量&#xff0c;是竞技体育与社会发生关联&#xff0c;并作用于社会的媒…

基于OpenCV的谷物颗粒识别

基于OpenCV的谷物颗粒识别 一、程序整体功能介绍1.1 导入库与函数定义1.2 颜色分割与灰度处理1.3 二值化与轮廓检测1.4 绘制与计数1.5 主程序与结果展示 二、算法原理与实现流程2.1算法原理&#xff08;1&#xff09;颜色分割&#xff08;2&#xff09;灰度处理与二值化&#x…

前端 TS 语法 接口(2)

介绍 TypeScript的核心原则之一是对值所具有的shape进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里&#xff0c;接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。 只读属性 readonly 一些对象属性只能在对象刚刚创建的…

Java反转单链表

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ //核心思想&#xff0c;利用cur和Curnex…

基础篇_数据持久化(实战-我的B站,MySQL数据库)

文章目录 一. 实战-我的B站1. 功能演示2. 设计数据类数据展示路径参数 3. 设计 Service 类静态资源映射读取文件的时机Stream API 改进 二. MySQL 数据库1. 数据库必要性2. MySQL 安装下载压缩包初始化数据库运行服务器运行客户端 3. 初步使用4. datagrip添加数据源导入数据用 …

HBase 基础

HBase 基础 HBase1. HBase简介1.1 HBase定义1.2 HBase数据模型1.2.1 HBase逻辑结构1.2.2 HBase物理存储结构1.2.3 数据模型 1.3 HBase基本架构 2. HBase环境安装2.1 HBase 安装部署2.1.1 HBase 本地按照2.1.2 HBase 伪分布模式安装2.1.3 HBase 集群安装 2.2 HBase Shell操作2.2…

jar包部署到linux虚拟机的docker中之后连不上mysql

前言&#xff1a; 跟着黑马学习docker的时候&#xff0c;将java项目部署到了docker中&#xff0c;运行访问报错&#xff0c;反馈连不上mysql。 错误描述&#xff1a; 方法解决&#xff1a; 概述&#xff1a;在虚拟中中&#xff0c;我进入项目容器的内部&#xff0c;尝试ping…

分布式搜索引擎--认识

elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 。 elasticsearch结合kibana、Logstash、Beats&#xff0c;也就是elastic stack&#xff08;ELK&#xff09;。被广泛…

【嵌入式移植】3、编译U-Boot

编译U-Boot 0 U-Boot及本文所选硬件1 获取U-Boot源码2 获取工具链3 BL314 编译4.1 yylloc4.2 u_boot_dtsi 5 烧写6 上电验证 0 U-Boot及本文所选硬件 Das U-Boot&#xff0c;全称 Universal Boot Loader&#xff0c;是遵循GPL条款的开放源码项目。U-Boot的作用是系统引导。U-B…

seata分布式事务(与dubbo集成)

1.seata是什么? Seata 是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 2.seata的注解 GlobalTransactional&#xff1a;全局事务注解&#xff0c;添加了以后可实现分布式事务的回滚和提交&#xff0c;用法与spring…

【.NET Core】Lazy<T> 实现延迟加载详解

【.NET Core】Lazy 实现延迟加载详解 文章目录 【.NET Core】Lazy<T> 实现延迟加载详解一、概述二、Lazy<T>是什么三、Lazy基本用法3.1 构造时使用默认的初始化方式3.2 构造时使用指定的委托初始化 四、Lazy.Value使用五、Lazy扩展用法5.1 实现延迟属性5.2 Lazy实现…

MySQL 日志之二进制日志-binlog

1、简介 MySQL 的二进制日志记录了对 MySQL 所有的更改操作&#xff0c;不包括 select 和 show 等操作。二进制日志文件主要有&#xff1a;数据恢复、主从复制、审计&#xff08;判断是否有注入攻击&#xff09;等作用。 2、二进制日志参数配置 2.1、文件参数配置 linux 中 My…

安装nvidia driver出现 the cc vision check falied

这里提示说的需要gcc12,但是我只有gcc11,所以就报错了&#xff0c;说一说我自己的解决方法&#xff1a; 安装gcc12和g12,再切换版本为gcc12 安装gcc12: sudo apt install gcc-12安装g12: sudo apt -y install g-12切换版本&#xff1a;参考博客

Linux的SSH服务

一.SSH服务简介 1.什么是SSH SSH&#xff08;Secure Shell&#xff09;是一种安全通道协议&#xff0c;主要用来实现字符界面的远程登录、远程复制等功能。SSH 协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令&#xff0c;SSH 为建立在应…