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` 扩展:
quarkus extension add narayana-lra,resteasy-jackson,resteasy-client-jackson
./mvnw quarkus:add-extension -Dextensions='narayana-lra,resteasy-jackson,resteasy-client-jackson'
./gradlew addExtension --extensions='narayana-lra,resteasy-jackson,resteasy-client-jackson'
这会将以下内容添加到您的构建文件中
<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>
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 中有更多示例。