游戏迷提供最新游戏下载和手游攻略!

利用Spring Boot 3与Grafana构建高效的可观测系统

发布时间:2024-10-18浏览:58

Spring Boot 2 和 3 之间的可观察性方法发生了一些重大变化。通过 Spring Cloud Sleuth 项目,跟踪将不再是 Spring Cloud 的一部分。该项目的核心已移至Micrometer Tracing。您可以在 Spring 博客上的这篇文章中阅读有关原因和未来计划的更多信息。

那篇文章的主要目的是给你一个简单的收据,告诉你如何使用一种新的方法为你用 Spring Boot 编写的微服务启用可观察性。为了简化我们的练习,我们将在他们的云中使用完全托管的 Grafana 实例。我们将构建一个非常基本的架构,其中包含两个在本地运行的微服务。让我们花点时间详细讨论一下我们的架构。

源代码

如果您想自己尝试一下,可以随时查看我的源代码。为此,您需要克隆我的 GitHub 存储库。它包含几个教程。您需要转到inter-communication目录。之后,您应该按照我的指示进行操作。

https://github.com/piomin/course-spring-microservices.git

Spring Boot 可观察性架构

有两个应用程序:inter-callme-service和inter-caller-service。应用程序调用应用程序公开的inter-caller-serviceHTTP 端点inter-callme-service。我们运行两个inter-callme-service. 我们还将使用 Spring Cloud Load Balancer 在这两个实例之间配置静态负载平衡。所有应用程序都将使用 Spring Boot Actuator 和 Micrometer 项目公开 Prometheus 指标。对于追踪,我们将使用 Open Telemetry with Micrometer Tracing 和 OpenZipkin。为了将包括日志、指标和跟踪在内的所有数据从本地 Spring Boot 实例发送到云端,我们必须使用 Grafana Agent。

另一方面,有一个堆栈负责收集和可视化所有数据。正如我之前提到的,我们将为此利用 Grafana Cloud。这是一种非常舒适的方式,因为我们不必安装和配置所有必需的工具。首先,Grafana Cloud 提供了一个现成的 Prometheus 实例,负责收集指标。我们还需要一个日志聚合工具来存储和查询我们应用程序的日志。Grafana Cloud 为此提供了一个预配置的 Loki 实例。最后,我们有一个通过 Grafana Tempo 的分布式跟踪后端。这是我们整个架构的可视化。

使用千分尺启用度量和跟踪

为了以 Prometheus 格式导出指标,我们需要包含micrometer-registry-prometheus依赖项。为了跟踪上下文传播,我们应该添加micrometer-tracing-bridge-otel模块。我们还应该使用 Grafana Tempo 支持的格式之一导出跟踪范围。它将通过opentelemetry-exporter-zipkin依赖项成为 OpenZipkin。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId></dependency><dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-otel</artifactId></dependency><dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-zipkin</artifactId></dependency>

我们需要使用最新可用的 Spring Boot 3 版本。目前,它是3.0.0-RC1. 作为候选版本,该版本在 Spring Milestone 存储库中可用。

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0-RC1</version> <relativePath/></parent>

Spring Boot 3 中更有趣的新特性之一是对 Prometheus 示例的支持。示例是对应用程序发布的指标之外的数据的引用。它们允许将度量数据链接到分布式跟踪。在这种情况下,发布的指标包含对traceId. 为了启用特定指标的示例,我们需要公开百分位数直方图。我们将为http.server.requests度量(1)这样做。我们还将通过将采样概率设置为 1.0 (2)将所有跟踪发送到 Grafana Cloud 。最后,为了验证它是否正常工作,我们打印traceId并spanId在日志行(3)中。

spring: application: name: inter-callme-service output.ansi.enabled: ALWAYSmanagement: endpoints.web.exposure.include: '*' metrics.distribution.percentiles-histogram.http.server.requests: true # (1) tracing.sampling.probability: 1.0 # (2)logging.pattern.console: "%clr(%d{HH:mm:ss.SSS}){blue} %clr(%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]){yellow} %clr(:){red} %clr(%m){faint}%n" # (3)

公开 POST 端点,该inter-callme-service端点仅以相反的顺序返回消息。我们不需要在这里添加任何东西,只需要标准的 Spring Web 注释。

@RestController@RequestMapping("/callme")class CallmeController(private val repository: ConversationRepository) { private val logger: Logger = LoggerFactory .getLogger(CallmeController::class.java) @PostMapping("/call") fun call(@RequestBody request: CallmeRequest) : CallmeResponse { logger.info("In: {}", request) val response = CallmeResponse(request.id, request.message.reversed()) repository.add(Conversation(request = request, response = response)) return response }}

使用 Spring Cloud 进行负载平衡

@RestController@RequestMapping("/caller")class CallerController(private val template: RestTemplate) { private val logger: Logger = LoggerFactory .getLogger(CallerController::class.java) private var id: Int = 0 @PostMapping("/send/{message}") fun send(@PathVariable message: String): CallmeResponse? { logger.info("In: {}", message) val request = CallmeRequest(++id, message) return template.postForObject("http://inter-callme-service/callme/call", request, CallmeResponse::class.java) }}

在本练习中,我们将使用一个静态客户端负载均衡器,将流量分配给inter-callme-service. 通常,您会将 Spring Cloud Load Balancer 与基于 Eureka 的服务发现集成。但是,我不想让我们的演示与架构中的外部组件复杂化。假设我们正在运行inter-callme-service,这是文件55800中55900的负载均衡器配置application.yml:

spring: application: name: inter-caller-service cloud: loadbalancer: cache: ttl: 1s instances: - name: inter-callme-service servers: localhost:55800, localhost:55900

由于没有内置的静态负载均衡器实现,我们需要添加一些代码。首先,我们必须将配置属性注入 Spring bean。

@Configuration@ConfigurationProperties("spring.cloud.loadbalancer")class LoadBalancerConfigurationProperties { val instances: MutableList<ServiceConfig> = mutableListOf() class ServiceConfig { var name: String = "" var servers: String = "" }}

然后我们需要创建一个实现ServiceInstanceListSupplier接口的bean。它只返回ServiceInstance代表所有已定义静态地址的对象列表。

class StaticServiceInstanceListSupplier(private val properties: LoadBalancerConfigurationProperties, private val environment: Environment): ServiceInstanceListSupplier { override fun getServiceId(): String = environment .getProperty(LoadBalancerClientFactory.PROPERTY_NAME)!! override fun get(): Flux<MutableList<ServiceInstance>> { val serviceConfig: LoadBalancerConfigurationProperties.ServiceConfig? = properties.instances.find { it.name == serviceId } val list: MutableList<ServiceInstance> = serviceConfig!!.servers.split(",", ignoreCase = false, limit = 0) .map { StaticServiceInstance(serviceId, it) }.toMutableList() return Flux.just(list) }}

最后,我们需要为应用启用 Spring Cloud Load Balancer 并RestTemplate使用@LoadBalanced.

@SpringBootApplication@LoadBalancerClient(value = "inter-callme-service", configuration = [CustomCallmeClientLoadBalancerConfiguration::class])class InterCallerServiceApplication { @Bean @LoadBalanced fun template(builder: RestTemplateBuilder): RestTemplate = builder.build()}

这是客户端负载均衡器配置。我们提供我们的自定义StaticServiceInstanceListSupplier实现作为默认值ServiceInstanceListSupplier。然后我们将RandomLoadBalancer负载平衡算法设置为默认实现。

class CustomCallmeClientLoadBalancerConfiguration { @Autowired lateinit var properties: LoadBalancerConfigurationProperties @Bean fun clientServiceInstanceListSupplier( environment: Environment, context: ApplicationContext): ServiceInstanceListSupplier { val delegate = StaticServiceInstanceListSupplier(properties, environment) val cacheManagerProvider = context .getBeanProvider(LoadBalancerCacheManager::class.java) return if (cacheManagerProvider.ifAvailable != null) { CachingServiceInstanceListSupplier(delegate, cacheManagerProvider.ifAvailable) } else delegate } @Bean fun loadBalancer(environment: Environment, loadBalancerClientFactory: LoadBalancerClientFactory): ReactorLoadBalancer<ServiceInstance> { val name: String? = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME) return RandomLoadBalancer( loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier::class.java), name!! ) }}

使用 Spring Boot 测试可观察性

让我们看看它是如何工作的。第一步,我们将运行两个inter-callme-service. 由于我们设置了侦听端口的静态值,因此我们需要覆盖server.port每个实例的属性。我们可以使用 env 变量来做到这一点SERVER_PORT。转到inter-communication/inter-callme-service目录并运行以下命令:

$ SERVER_PORT=55800 mvn spring-boot:run$ SERVER_PORT=55900 mvn spring-boot:run

然后,转到inter-communication/inter-caller-service目录并在默认端口上运行单个实例8080:

$ mvn spring-boot:run

POST /caller/send/{message}然后,让我们用参数多次调用我们的端点,例如:

$ curl -X POST http://localhost:8080/caller/call/hello1

以下是inter-caller-service带有突出显示的traceId参数值的日志:

让我们看一下来自 的日志inter-callme-service。如您所见,该参数与该请求的traceId参数相同。traceIdinter-caller-service

以下是第二个实例的日志inter-callme-service:

您也可以使用 Spring Cloud OpenFeign 尝试相同的练习。它已配置并可以使用。但是,对我来说,它没有traceId正确传播参数。也许,当前非 GA 版本的 Spring Boot 和 Spring Cloud 就是这种情况。

让我们验证另一个特性——Prometheus 示例。为此,我们需要使用请求 OpenMetrics 格式/actuator/prometheus的标头调用端点。Accept这与 Prometheus 用于抓取指标的标头相同。

$ curl -H 'Accept: application/openmetrics-text; version=1.0.0' http://localhost:8080/actuator/prometheus

如您所见,结果包含几个指标traceId和spanId参数。这些是我们的榜样。

安装和配置 Grafana 代理

$ brew install grafana-agent

在我们启动代理之前,我们需要准备一个配置文件。该文件的位置还取决于您的操作系统。对我来说是$(brew --prefix)/etc/grafana-agent/config.yml。配置 YAML 清单包含有关我们希望如何收集和发送指标、跟踪和日志的信息。让我们从指标开始。在该scrape_configs部分中,我们需要设置用于抓取的端点列表(1)和默认路径(2)。在该remote_write部分中,我们必须传递 Grafana Cloud 实例身份验证凭据(3)和 URL (4)。默认情况下,Grafana Agent 不发送示例。因此我们需要使用send_exemplars属性(5)来启用它。

metrics: configs: - name: integrations scrape_configs: - job_name: agent static_configs: - targets: ['127.0.0.1:55800','127.0.0.1:55900','127.0.0.1:8080'] # (1) metrics_path: /actuator/prometheus # (2) remote_write: - basic_auth: # (3) password: <YOUR_GRAFANA_CLOUD_TOKEN> username: <YOUR_GRAFANA_CLOUD_USER> url: https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push # (4) send_exemplars: true # (5) global: scrape_interval: 60s

您可以在 Grafana Cloud 仪表板中找到有关您的 Prometheus 实例的所有信息。

在下一步中,我们准备用于收集日志并将其发送到 Grafana Loki 的配置。和之前一样,我们需要设置 Loki 实例的身份验证凭据(1)和 URL (2)。这里最重要的是传递日志文件的位置(3)。

logs: configs: - clients: - basic_auth: # (1) password: <YOUR_GRAFANA_CLOUD_TOKEN> username: <YOUR_GRAFANA_CLOUD_USER> # (2) url: https://logs-prod-eu-west-0.grafana.net/loki/api/v1/push name: springboot positions: filename: /tmp/positions.yaml scrape_configs: - job_name: springboot-json static_configs: - labels: __path__: ${HOME}/inter-*/spring.log # (3) job: springboot-json targets: - localhost target_config: sync_period: 10s

默认情况下,Spring Boot 仅记录到控制台,不写入日志文件。在我们的例子中,Grafana 代理从输出文件中读取日志行。为了写入日志文件,我们需要设置一个 logging.file.name or logging.file.path 属性。由于有两个实例,inter-callme-service我们需要以某种方式区分它们的日志文件。我们将server.port为此使用该属性。文件内的日志以 JSON 格式存储。

logging: pattern: console: "%clr(%d{HH:mm:ss.SSS}){blue} %clr(%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]){yellow} %clr(:){red} %clr(%m){faint}%n" file: "{\"timestamp\":\"%d{HH:mm:ss.SSS}\",\"level\":\"%p\",\"traceId\":\"%X{traceId:-}\",\"spanId\":\"%X{spanId:-}\",\"appName\":\"${spring.application.name}\",\"message\":\"%m\"}%n" file: path: ${HOME}/inter-callme-service-${server.port}

最后,我们将配置跟踪收集。除了 Grafana Tempo 实例的身份验证凭据和 URL,我们还需要启用 OpenZipkin 接收器(1)。

traces: configs: - name: default remote_write: - endpoint: tempo-eu-west-0.grafana.net:443 basic_auth: username: <YOUR_GRAFANA_CLOUD_USER> password: <YOUR_GRAFANA_CLOUD_TOKEN> # (1) receivers: zipkin:

然后,我们可以使用以下命令启动代理:

$ brew services restart grafana-agent

Grafana 代理包含一个侦听默认端口的 Zipkin 收集器9411。还有一个 HTTP API 暴露在代理之外的端口上,12345用于验证代理状态。

例如,我们可以使用 Grafana Agent HTTP API 来验证它正在监控多少日志文件。为此,只需调用端点GET agent/api/v1/logs/targets。如您所见,对我来说,它正在监视三个文件。这正是我们想要实现的目标,因为有两个正在运行的实例inter-callme-service和一个inter-caller-service.

使用 Grafana Stack 可视化 Spring Boot 可观察性

Grafana Cloud 在我们的练习中最重要的优势之一是我们已经配置了所有必需的东西。导航到 Grafana 仪表板后,您可以显示可用数据源的列表。如您所见,已经配置了 Loki、Prometheus 和 Tempo。

默认情况下,Grafana Cloud 在 Prometheus 数据源中启用示例。如果您自己运行 Grafana,请不要忘记在您的 Prometheus 数据源上启用它。

让我们从日志开始。我们将分析与“使用 Spring Boot 测试可观察性”部分完全相同的日志。我们将获取 Grafana Agent 发送的所有日志。您可能还记得,我们将所有日志格式化为 JSON。因此,我们将使用Json服务器端的解析器来解析它们。多亏了这一点,我们将能够按所有日志字段进行过滤。例如,我们可以使用traceId标签过滤表达式:{job="springboot-json"} | json | traceId = 1bb1d7d78a5ac47e8ebc2da961955f87.

这是未经任何过滤的完整日志列表。突出显示的行包含两个分析轨迹的日志。

在下一步中,我们将配置 Prometheus 指标可视化。由于我们为指标启用了百分位直方图,因此我们有多个由值http.server.requests表示的桶。 一组带有标签 http_server_requests_seconds_bucket的多个桶 ,其中包含 值小于或等于 标签中包含的数值的所有样本的 计数 。我们需要统计 90% 和 60% 的请求的直方图。以下是我们的 Prometheus 查询:_bucketlele

这是我们的直方图。示例显示为绿色菱形。

当您将鼠标悬停在所选示例上时,您将看到更多详细信息。例如,它包括traceId值。

最后,我们练习的最后一部分。我们想用 Grafana Tempo 分析特定的痕迹。您唯一需要做的就是选择grafanacloud-*-traces数据源并设置 searched 的值traceId。这是一个示例结果。

最后的想法

用户评论

愁杀

这个游戏名字好奇怪啊,感觉像是技术术语组合在一起。

    有9位网友表示赞同!

屌国女农

春天和靴子的结合很有意思,不知道游戏里会有什么独特的元素。

    有10位网友表示赞同!

还未走i

看来这游戏侧重于展示技术和画面,是不是很专业呢?

    有9位网友表示赞同!

独角戏°

Grafana应该是用来监控的,那这个游戏能实时呈现数据吗?

    有5位网友表示赞同!

浮殇年华

Boot 3听起来挺高级的,会不会有高科技玩法?

    有20位网友表示赞同!

颜洛殇

不知道为什么把Spring Boot和Grafana放在一起,会有什么意想不到的效果。

    有19位网友表示赞同!

南宫沐风

听起来像是技术宅的游戏,不知道有多少人愿意玩呢?

    有19位网友表示赞同!

■孤独像过不去的桥≈

这个游戏名字让我想到了自动化监控管理,也许是个模拟类游戏吧。

    有13位网友表示赞同!

执念,爱

是不是在游戏中能感受到实时数据监控带来的快感和成就感呢?

    有20位网友表示赞同!

巷陌繁花丶

这游戏的操作看起来会很复杂,需要一定的技术背景才能驾驭。

    有17位网友表示赞同!

留我一人

感觉这个游戏很适合做教育用途,普及数据分析知识。

    有14位网友表示赞同!

醉枫染墨

喜欢这种结合实际应用的游戏,希望能在里面学到一些新技能。

    有7位网友表示赞同!

你的眸中有星辰

游戏名字虽奇怪,但好奇心驱使我想要尝试一把。

    有9位网友表示赞同!

Hello爱情风

希望能有个简洁明了的介绍,让我快速了解游戏的本质。

    有11位网友表示赞同!

夏以乔木

Grafana可观察性听起来很厉害,不知道具体有什么亮点?

    有9位网友表示赞同!

拽年很骚

这游戏的名字吸引了我,希望能有更多的详细信息曝光。

    有8位网友表示赞同!

北染陌人

感觉这个游戏可能有些小众,但我觉得一定很有趣。

    有12位网友表示赞同!

青山暮雪

希望游戏里有丰富的数据图表,让我们能更直观地了解游戏的奥秘。

    有13位网友表示赞同!

从此我爱的人都像你

看起来这个游戏会是一款极具挑战性的技术之旅!

    有11位网友表示赞同!

窒息

期待更多关于这个游戏的技术分享和教程。

    有5位网友表示赞同!

热点资讯