编辑此页面

Narayana LRA 参与者支持

简介

LRA(长时运行操作)参与者扩展在微服务设计中非常有用,因为不同的服务可以受益于宽松的分布式一致性概念。

其理念是让多个服务协调执行不同的计算/操作,同时保留在计算过程中执行的任何操作的补偿选项。这种松散的服务耦合弥合了 JTA/XA 等强一致性模型与“自创”的临时一致性解决方案之间的差距。

该模型基于 Eclipse MicroProfile LRA 规范。方法是开发人员使用 Java 注释(@LRA)来注解业务方法。当调用 such a method 时,会创建一个 LRA 上下文(如果尚不存在),该上下文会随着后续的 Jakarta REST 调用一起传递,直到达到一个也包含 LRA 的 `@LRA` 注释的方法,该注释具有指示 LRA 应关闭或取消的属性。默认情况下,LRA 将在启动 LRA 的同一方法中关闭(该方法本身可能在方法执行期间传播了上下文)。 Jakarta REST 资源通过至少一个方法带有 @Compensate 注释来表明其希望参与交互。如果上下文稍后被取消,那么即使发生故障,也保证会调用此 `@Compensate` 操作,并且它是资源对其在 LRA 上下文中执行的任何活动进行补偿的触发器。此保证使服务能够可靠地运行,并确保最终一致性(当所有补偿活动都已成功完成时)。参与者可以通过带有 @Complete 注释的方法来请求在参与的 LRA 关闭时获得可靠通知。通过这种方式,取消 LRA 会通过其 Compensate 回调通知所有参与者,而关闭 LRA 会通过其 Complete 回调(如果存在)通知所有参与者。有关控制参与者的其他注释记录在 MicroProfile LRA API v1.0 javadoc 中。

配置

配置好 Quarkus Maven 项目后,您可以通过在项目根目录中运行以下命令来添加 `narayana-lra` 扩展:

CLI
quarkus extension add narayana-lra,resteasy-jackson,resteasy-client-jackson
Maven
./mvnw quarkus:add-extension -Dextensions='narayana-lra,resteasy-jackson,resteasy-client-jackson'
Gradle
./gradlew addExtension --extensions='narayana-lra,resteasy-jackson,resteasy-client-jackson'

这会将以下内容添加到您的构建文件中

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-narayana-lra</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-client-jackson</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-narayana-lra")
implementation("io.quarkus:quarkus-resteasy-jackson")
implementation("io.quarkus:quarkus-resteasy-client-jackson")

`quarkus-narayana-lra` 需要与服务器 Jakarta REST 实现和 REST Client 实现配合才能工作。这意味着用户还应该在其应用程序中拥有 `quarkus-resteasy-jackson` 和 `quarkus-resteasy-client-jackson` 或 `quarkus-rest-jackson` 和 `quarkus-rest-client-jackson` 依赖项。

如果存在正在运行的协调器,那么您只需此配置即可创建新的 LRA 并将参与者与之关联。

可以通过在 `src/main/resources` 目录中更新 `application.properties` 文件来配置 LRA 扩展。唯一的 LRA 特定属性是 `quarkus.lra.coordinator-url=<url>`,它指定了一个外部协调器的 HTTP 端点,例如:

quarkus.lra.coordinator-url=https://:8080/lra-coordinator

对于 Narayana 协调器,URL 的路径组件通常是 `lra-coordinator`。协调器可以从 https://quay.io/repository/jbosstm/lra-coordinator 获取,或者您可以使用包含适当依赖项的 Maven pom 来构建自己的协调器。将提供一个 Quarkus 快速入门示例来说明如何做到这一点,或者您可以查看 Narayana 快速入门 之一。另一种选择是在 WildFly 应用程序服务器内对其进行托管。

处理故障

当 LRA 被告知完成时,即当调用带有 `@LRA(end = true, …​)` 注释的方法时,协调器将指示所有参与交互的服务完成。如果服务不可用(或仍在完成中),则协调器将定期重试。用户有责任在首次加入 LRA 时使用的端点上重新启动失败的服务,或者告知协调器他们希望在新端点上收到通知。LRA 在 **所有** 参与者确认其已完成之前,都不会被视为已完成。

协调器负责可靠地创建和结束 LRA 以及管理参与者注册,因此它必须可用(例如,如果它或网络发生故障,则环境负责分别重新启动协调器或修复网络)。为了完成此任务,协调器必须能够通过文件系统或数据库访问其日志的持久存储。在撰写本文时,管理协调器是用户的责任。一个“开箱即用”的解决方案即将推出。

示例

以下是如何启动 LRA 以及如何在 LRA 稍后被取消(调用 `@Compensate` 注释的方法)或关闭(调用 `@Complete`)时接收通知的简单示例:

@Path("/")
@ApplicationScoped
public class SimpleLRAParticipant
{
    @LRA(LRA.Type.REQUIRES_NEW) // a new LRA is created on method entry
    @Path("/work")
    @PUT
    public void doInNewLongRunningAction(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId)
    {
        /*
         * Perform business actions in the context of the LRA identified by the
         * value in the injected Jakarta REST header. This LRA was started just before
         * the method was entered (REQUIRES_NEW) and will be closed when the
         * method finishes at which point the completeWork method below will be
         * invoked.
         */
    }

    @org.eclipse.microprofile.lra.annotation.Complete
    @Path("/complete")
    @PUT
    public Response completeWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId,
                                 String userData)
    {
        /*
         * Free up resources allocated in the context of the LRA identified by the
         * value in the injected Jakarta REST header.
         *
         * Since there is no @Status method in this class, completeWork MUST be
         * idempotent and MUST return the status.
         */
         return Response.ok(ParticipantStatus.Completed.name()).build();
    }

    @org.eclipse.microprofile.lra.annotation.Compensate
    @Path("/compensate")
    @PUT
    public Response compensateWork(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId,
                                   String userData)
    {
        /*
         * The LRA identified by the value in the injected Jakarta REST header was
         * cancelled so the business logic should compensate for any actions
         * that have been performed while running in its context.
         *
         * Since there is no @Status method in this class, compensateWork MUST be
         * idempotent and MUST return the status
         */
         return Response.ok(ParticipantStatus.Compensated.name()).build();
    }
}

该示例还表明,当存在 LRA 时,可以通过 Jakarta REST 注释类型 `@HeaderParam` 读取请求头来获取其标识符。

这是一个如何在资源方法中启动 LRA,并在另一资源方法中使用 `LRA` 注释的 `end` 元素关闭它的示例。它还展示了如何配置 LRA,使其在业务方法返回 `cancelOn` 和 `cancelOnFamily` 元素中标识的特定 HTTP 状态码时自动取消:

  @LRA(value = LRA.Type.REQUIRED, // if there is no incoming context a new one is created
       cancelOn = {
           Response.Status.INTERNAL_SERVER_ERROR // cancel on a 500 code
       },
       cancelOnFamily = {
           Response.Status.Family.CLIENT_ERROR // cancel on any 4xx code
       },
       end = false) // the LRA will continue to run when the method finishes
  @Path("/book")
  @POST
  public Response bookTrip(...) { ... }

  @LRA(value = LRA.Type.MANDATORY, // requires an active context before method can be executed
       end = true) // end the LRA started by the bookTrip method
  @Path("/confirm")
  @PUT
  public Booking confirmTrip(Booking booking) throws BookingException { ... }

bookTrip 方法上的 `end = false` 元素强制 LRA 在方法完成时继续运行,而 confirmTrip 方法上的 `end = true` 元素强制 LRA(由 bookTrip 方法启动)在方法完成时关闭。请注意,此 `end` 元素可以放置在任何 Jakarta REST 资源上(即一个服务可以启动 LRA,而另一个服务可以结束它)。在 MicroProfile LRA 规范文档MicroProfile LRA TCK 中有更多示例。

相关内容