The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Apoio ao Participante do Narayana LRA

Introdução

A extensão do participante LRA (abreviação de Long Running Action) é útil em projetos baseados em microsserviços, nos quais diferentes serviços podem se beneficiar de uma noção relaxada de consistência distribuída.

A ideia é que vários serviços executem diferentes cálculos/ações em conjunto, mantendo a opção de compensar quaisquer ações executadas durante o cálculo. Esse tipo de acoplamento frouxo de serviços preenche a lacuna entre modelos de consistência fortes, como JTA/XA, e soluções de consistência ad hoc "caseiras".

The model is based on the Eclipse MicroProfile LRA specification. The approach is for the developer to annotate a business method with a Java annotation (@LRA). When such a method is called, an LRA context is created (if one is not already present) which is passed along with subsequent JAX-RS invocations until a method is reached which also contains an @LRA annotation with an attribute that indicates that the LRA should be closed or cancelled. The default is for the LRA to be closed in the same method that started the LRA (which itself may have propagated the context during method execution). The JAX-RS resource indicates that it wishes to participate in the interaction by, minimally, marking one of the methods with an @Compensate annotation. If the context is later cancelled, then this @Compensate action is guaranteed to be called even in the presence of failures and is the trigger for the resource to compensate for any activities it performed in the context of the LRA. This guarantee enables services to operate reliably with the assurance of eventual consistency (when all compensation activities have ran to completion). The participant can ask to be reliably notified when the LRA it is participating in is closed by marking one of the methods with an @Complete annotation. In this way cancelling an LRA causes all participants to be notified via their Compensate callback and closing an LRA causes all participants to be notified via their Complete callback (if they have one). Other annotations for controlling participants are documented in the MicroProfile LRA API v1.0 javadoc.

Configuração

Depois de configurar o seu projeto Quarkus Maven, é possível adicionar a extensão narayana-lra executando o seguinte comando no diretório base do seu projeto:

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

Isto irá adicionar o seguinte trecho no seu arquivo de build:

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-rest-client-jackson</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-narayana-lra")
implementation("io.quarkus:quarkus-resteasy-jackson")
implementation("io.quarkus:quarkus-rest-client-jackson")

quarkus-narayana-lra needs to be complemented with a server JAX-RS implementation and a REST Client implementation in order to work. This means that users should also have either quarkus-resteasy-jackson and quarkus-rest-client-jackson or quarkus-resteasy-reactive-jackson and quarkus-rest-client-reactive-jackson dependencies in their application.

Se exitir um coordenador em execução, então isto é tudo o que você precisa para criar novas LRAs e alistar participantes nelas.

A extensão LRA pode ser configurada atualizando um arquivo application.properties no diretório src/main/resources . A única propriedade específica do LRA é quarkus.lra.coordinator-url=<url> , que especifica o ponto de extremidade HTTP de um coordenador externo, por exemplo:

quarkus.lra.coordinator-url=http://localhost:8080/lra-coordinator

Para um coordenador Narayana, o componente de caminho da url é normalmente lra-coordinator . Os coordenadores podem ser obtidos em https://quay.io/repository/jbosstm/lra-coordinator ou você pode criar seu próprio coordenador usando um maven pom que inclua as dependências apropriadas. Um quickstart do Quarkus será fornecido para mostrar como fazer isso, ou você pode dar uma olhada em um dos quickstarts do Narayana . Outra opção seria executá-lo gerenciado dentro de um servidor de aplicativos WildFly.

Manuseio de falhas

Quando uma LRA é instruída a finalizar, ou seja, quando um método anotado com @LRA(end = true, …​) é invocado, o coordenador instruirá todos os serviços envolvidos na interação a finalizar. Se um serviço estiver indisponível (ou ainda finalizando), o coordenador tentará novamente periodicamente. É responsabilidade do usuário reiniciar os serviços com falha no mesmo ponto de extremidade que usaram quando se juntaram à LRA pela primeira vez, ou informar ao coordenador que desejam ser notificados em novos pontos de extremidade. Uma LRA não é considerada finalizada até que todos os participantes tenham confirmado que finalizaram.

O coordenador é responsável por criar e finalizar LRAs de forma confiável e por gerenciar o de participantes e, portanto, deve estar disponível (por exemplo, se ele ou a rede falharem, algo no ambiente será responsável por reiniciar o coordenador ou por reparar a rede, respectivamente). Para cumprir essa tarefa, o coordenador deve ter acesso a um armazenamento durável para seus registros (por meio de um sistema de arquivos ou em um banco de dados). No momento em que este artigo foi escrito, o gerenciamento de coordenadores é responsabilidade do usuário. Uma solução "pronta para uso" será apresentada em breve.

Exemplos

A seguir, um exemplo simples de como iniciar um LRA e como receber uma notificação quando o LRA for cancelado posteriormente (o método anotado @Compensate é chamado) ou fechado ( @Complete é chamado):

@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 JAX-RS 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 JAX-RS 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 JAX-RS 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();
    }
}

The example also shows that when an LRA is present its identifier can be obtained by reading the request headers via the @HeaderParam JAX-RS annotation type.

E aqui está um exemplo de como iniciar um LRA em um método de recurso e fechá-lo em um método de recurso diferente usando o elemento end da anotação LRA . Ele também mostra como configurar o LRA para ser cancelado automaticamente se o método de negócios retornar os códigos de status HTTP específicos identificados nos elementos cancelOn e cancelOnFamily :

  @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 { ... }

The end = false element on the bookTrip method forces the LRA to continue running when the method finishes and the end = true element on the confirmTrip method forces the LRA (started by the bookTrip method) to be closed when the method finishes. Note that this end element can be placed on any JAX-RS resource (ie one service can start the LRA whilst a different service ends it). There are many more examples in the Microprofile LRA specification document and in the Microprofile LRA TCK.