当前位置:Java -> 使用 X-Ray 分析和调试基于 Quarkus 的 AWS Lambda 函数

使用 X-Ray 分析和调试基于 Quarkus 的 AWS Lambda 函数

无服务器架构已成为构建快速、可伸缩和高效应用的范式转变方法。虽然无服务器架构提供了无与伦比的灵活性,但在监控和故障排除方面也引入了新的挑战。

在本文中,我们将探讨Quarkus如何与AWS X-Ray集成,以及如何使用Jakarta CDI Interceptor来保持您的代码清晰,同时添加自定义仪器。

Quarkus和AWS Lambda

Quarkus是一个专为GraalVM和HotSpot定制的基于Java的框架,可以实现惊人的快速启动时间,并具有极低的内存占用率。它提供了几乎立即的规模扩展和高密度的内存利用率,这对于像Kubernetes这样的容器编排平台或AWS Lambda这样的无服务器运行时非常有用。

构建AWS Lambda函数可以像启动Quarkus项目、添加quarkus-amazon-lambda依赖项,并定义您的AWS Lambda Handler函数那样简单。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-amazon-lambda</artifactId>
</dependency>


可以在官方Quarkus AWS Lambda指南中找到有关如何使用Quarkus开发AWS Lambda函数的详尽指南。

为Lambda函数启用X-Ray

Quarkus提供了对X-Ray的开箱即用支持,但您需要向项目添加一个依赖项并配置一些设置,以使其与GraalVM/原生编译的Quarkus应用程序配合使用。

首先,让我们添加quarkus-amazon-lambda-xray依赖项。

<!-- adds dependency on required x-ray classes and adds support for graalvm native -->
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-amazon-lambda-xray</artifactId>
</dependency>


不要忘记为您的Lambda函数启用跟踪,否则它将无法正常工作。一个实现的例子是在AWS CDK代码中将跟踪参数设置为active

function = Function.Builder.create(this, "feed-parsing-function")
      ...
      .memorySize(512)
      .tracing(Tracing.ACTIVE)
      .runtime(Runtime.PROVIDED_AL2023)
      .logRetention(RetentionDays.ONE_WEEK)
      .build();


在部署函数和函数调用之后,您应该能够在Cloudwatch界面中看到来自X-Ray的跟踪。默认情况下,它将显示函数的一些基本时间信息,如初始化和调用持续时间。


添加更多仪器

现在依赖项已经就位,并且我们的函数已启用跟踪,我们可以通过利用X-Ray SDK的TracingIntercepter来丰富X-Ray中的跟踪。例如,对于SQS和DynamoDB客户端,您可以在application.properties文件内显式设置拦截器。

quarkus.dynamodb.async-client.type=aws-crt
quarkus.dynamodb.interceptors=com.amazonaws.xray.interceptors.TracingInterceptor
quarkus.sqs.async-client.type=aws-crt
quarkus.sqs.interceptors=com.amazonaws.xray.interceptors.TracingInterceptor


在放置这些属性、重新部署并执行函数后,TracingIntercepter将包装每个对SQS和DynamoDB的API调用,并将实际的跟踪信息与跟踪一起存储。

这对于调试非常有用,因为它允许您验证代码并检查任何错误。请求AWS服务是计费模型的一部分,因此如果您的代码出现错误并且调用次数过多,可能会变得非常昂贵。

自定义子段

使用AWS SDK的TracingInterceptor配置后,我们可以获取关于对AWS API的调用的信息,但如果我们想看到关于我们自己代码或AWS之外服务的远程调用的信息怎么办?

Java X-Ray SDK支持向跟踪添加自定义子段的概念。您可以通过以下代码片段中看到的几行代码将子段添加到跟踪中。

public void someMethod(String argument)  {
  // wrap in subsegment
  Subsegment subsegment = AWSXRay.beginSubsegment("someMethod");
  try {
      // Your business logic
  } catch (Exception e) {
     subsegment.addException(e);
     throw e;
  } finally {
     AWSXRay.endSubsegment();
  }
}


尽管这样做很容易,但如果您有很多方法需要应用跟踪,它将变得非常混乱。这并不理想,最好不要将自己的代码与X-Ray仪器混合在一起。

Quarkus和Jakarta CDI拦截器

Quarkus的编程模型基于Jakarta Contexts and Dependency Injection 4.0规范的Lite版本。除了依赖注入,该规范还描述了其他功能,比如:

  • 生命周期回调 — bean类可以声明生命周期@PostConstruct@PreDestroy回调。
  • 拦截器 — 用于将横切关注点与业务逻辑分离。
  • 装饰器 — 类似于拦截器,但由于它们实现了带有业务语义的接口,因此能够实现业务逻辑。
  • 事件和观察者 — Bean还可以产生和消费事件以完全解耦的方式进行交互。

正如上面提到的,CDI拦截器用于将横切关注点与业务逻辑分离。由于追踪是横切关注点,这听起来非常合适。让我们看看如何为我们的AWS X-Ray仪器创建拦截器。

如何为AWS X-Ray仪器创建拦截器

我们首先要定义我们的拦截器绑定,我们将其称为XRayTracing。拦截器绑定是中间注解,可用于将拦截器与目标bean关联。

package com.jeroenreijn.aws.quarkus.xray;

import jakarta.annotation.Priority;
import jakarta.interceptor.InterceptorBinding;

import java.lang.annotation.Retention;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@InterceptorBinding
@Retention(RUNTIME)
@Priority(0)
public @interface XRayTracing {
}


下一步是定义实际的拦截器逻辑,这是将为我们的业务逻辑添加附加的X-Ray指令(创建子段并将其包装在我们的业务逻辑周围)的代码。

package com.jeroenreijn.aws.quarkus.xray;

import com.amazonaws.xray.AWSXRay;
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;

@Interceptor
@XRayTracing
public class XRayTracingInterceptor {

    @AroundInvoke
    public Object tracingMethod(InvocationContext ctx) throws Exception {
        AWSXRay.beginSubsegment("## " + ctx.getMethod().getName());
        try {
            return ctx.proceed();
        } catch (Exception e) {
            AWSXRay.getCurrentSubsegment().addException(e);
            throw e;
        } finally {
            AWSXRay.endSubsegment();
        }
    }
}


拦截器的一个重要部分是@AroundInvoke注解,这意味着此拦截器代码将包装在我们自己的业务逻辑调用周围。

既然我们已经定义了我们的拦截器绑定和我们的拦截器,现在是时候开始使用它了。现在,我们想要为其创建子段的每个方法都可以用@XRayTracing注解进行注释。

    @XRayTracing
    public SyndFeed getLatestFeed() {
        InputStream feedContent = getFeedContent();
        return getSyndFeed(feedContent);
    }

    @XRayTracing
    public SyndFeed getSyndFeed(InputStream feedContent) {
        try {
            SyndFeedInput feedInput = new SyndFeedInput();
            return feedInput.build(new XmlReader(feedContent));
        } catch (FeedException | IOException e) {
            throw new RuntimeException(e);
        }
    }


看起来好多了。我得说,相当简洁。

基于跟踪的子段层级结构,X-Ray将能够显示具有时间信息的嵌套树结构。


结束语

Quarkus与X-Ray之间的集成非常容易启用。开发者体验在定义针对每个客户端的拦截器时非常棒。借助CDI拦截器的帮助,您可以保持代码整洁,而不用过多担心业务逻辑中的X-Ray特定代码。

构建自己的拦截器的另一种选择可能是开始使用AWS PowerTools for Lambda (Java)。Java的PowerTools是提高开发人员生产力的绝佳方法,但它不仅仅用于X-Ray,所以我会将其留到另一篇文章中。

推荐阅读: 百度面经(1)

本文链接: 使用 X-Ray 分析和调试基于 Quarkus 的 AWS Lambda 函数