Introdução aos Serviços Confiáveis em Java

Este artigo explica os conceitos básicos dos Serviços Confiáveis do Azure Service Fabric e orienta você na criação e implantação de um aplicativo de Serviço Confiável simples escrito em Java.

Confira nesta página um vídeo de treinamento que mostra como criar um serviço confiável sem monitoração de estado:

Instalação e configuração

Antes de começar, verifique se você tem o ambiente de desenvolvimento do Service Fabric configurado em sua máquina. Se você precisar configurá-lo, vá para Introdução ao Mac ou Introdução ao Linux.

Conceitos básicos

Para começar a usar os Serviços Confiáveis, você só precisa entender alguns conceitos básicos:

  • Tipo de serviço: Esta é a sua implementação de serviço. Ele é definido pela classe que você escreve que se estende StatelessService e qualquer outro código ou dependências usadas nele, juntamente com um nome e um número de versão.
  • Instância de serviço nomeada: para executar o serviço, você cria instâncias nomeadas do seu tipo de serviço, da mesma forma que cria instâncias de objeto de um tipo de classe. As instâncias de serviço são, na verdade, instanciações de objeto da sua classe de serviço que você escreve.
  • Host de serviço: as instâncias de serviço nomeadas que você cria precisam ser executadas dentro de um host. O host de serviço é apenas um processo onde as instâncias do seu serviço podem ser executadas.
  • Registo de serviços: O registo reúne tudo. O tipo de serviço deve ser registrado com o tempo de execução do Service Fabric em um host de serviço para permitir que o Service Fabric crie instâncias dele para execução.

Criar um serviço sem estado

Comece criando um aplicativo do Service Fabric. O SDK do Service Fabric para Linux inclui um gerador Yeoman para fornecer o andaime para um aplicativo do Service Fabric com um serviço sem monitoração de estado. Comece executando o seguinte comando Yeoman:

$ yo azuresfjava

Siga as instruções para criar um Serviço Apátrida Confiável. Para este tutorial, nomeie o aplicativo "HelloWorldApplication" e o serviço "HelloWorld". O resultado inclui diretórios para o HelloWorldApplication e HelloWorld.

HelloWorldApplication/
├── build.gradle
├── HelloWorld
│   ├── build.gradle
│   └── src
│       └── statelessservice
│           ├── HelloWorldServiceHost.java
│           └── HelloWorldService.java
├── HelloWorldApplication
│   ├── ApplicationManifest.xml
│   └── HelloWorldPkg
│       ├── Code
│       │   ├── entryPoint.sh
│       │   └── _readme.txt
│       ├── Config
│       │   └── _readme.txt
│       ├── Data
│       │   └── _readme.txt
│       └── ServiceManifest.xml
├── install.sh
├── settings.gradle
└── uninstall.sh

Registo do serviço

Os tipos de serviço devem ser registrados com o tempo de execução do Service Fabric. O tipo de serviço é definido na classe de serviço e sua que implementa StatelessServiceo ServiceManifest.xml . O registro do serviço é realizado no ponto de entrada principal do processo. Neste exemplo, o principal ponto de entrada do processo é HelloWorldServiceHost.java:

public static void main(String[] args) throws Exception {
    try {
        ServiceRuntime.registerStatelessServiceAsync("HelloWorldType", (context) -> new HelloWorldService(), Duration.ofSeconds(10));
        logger.log(Level.INFO, "Registered stateless service type HelloWorldType.");
        Thread.sleep(Long.MAX_VALUE);
    }
    catch (Exception ex) {
        logger.log(Level.SEVERE, "Exception in registration:", ex);
        throw ex;
    }
}

Implementar o serviço

Abra HelloWorldApplication/HelloWorld/src/statelessservice/HelloWorldService.java. Essa classe define o tipo de serviço e pode executar qualquer código. A API de serviço fornece dois pontos de entrada para o seu código:

  • Um método de ponto de entrada aberto, chamado runAsync(), onde você pode começar a executar quaisquer cargas de trabalho, incluindo cargas de trabalho de computação de longa execução.
@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
    ...
}
  • Um ponto de entrada de comunicação onde você pode conectar sua pilha de comunicação de escolha. É aqui que você pode começar a receber solicitações de usuários e outros serviços.
@Override
protected List<ServiceInstanceListener> createServiceInstanceListeners() {
    ...
}

Este tutorial se concentra no método de runAsync() ponto de entrada. É aqui que você pode começar imediatamente a executar seu código.

RunAsync

A plataforma chama esse método quando uma instância de um serviço é colocada e está pronta para ser executada. Para um serviço sem monitoração de estado, isso significa quando a instância de serviço é aberta. Um token de cancelamento é fornecido para coordenar quando sua instância de serviço precisa ser fechada. No Service Fabric, esse ciclo de abertura/fechamento de uma instância de serviço pode ocorrer muitas vezes ao longo do tempo de vida do serviço como um todo. Isso pode acontecer por vários motivos, incluindo:

  • O sistema move suas instâncias de serviço para balanceamento de recursos.
  • Ocorrem falhas no seu código.
  • O aplicativo ou sistema é atualizado.
  • O hardware subjacente sofre uma interrupção.

Essa orquestração é gerenciada pelo Service Fabric para manter seu serviço altamente disponível e devidamente equilibrado.

runAsync() não deve bloquear de forma síncrona. Sua implementação de runAsync deve retornar um CompletableFuture para permitir que o tempo de execução continue. Se sua carga de trabalho precisa implementar uma tarefa de longa execução que deve ser feita dentro do CompletableFuture.

Cancelamento

O cancelamento da sua carga de trabalho é um esforço cooperativo orquestrado pelo token de cancelamento fornecido. O sistema aguarda que a sua tarefa termine (por conclusão bem-sucedida, cancelamento ou falha) antes de avançar. É importante honrar o token de cancelamento, terminar qualquer trabalho e sair runAsync() o mais rápido possível quando o sistema solicitar o cancelamento. O exemplo a seguir demonstra como manipular um evento de cancelamento:

@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {

    // TODO: Replace the following sample code with your own logic
    // or remove this runAsync override if it's not needed in your service.

    return CompletableFuture.runAsync(() -> {
        long iterations = 0;
        while(true)
        {
        cancellationToken.throwIfCancellationRequested();
        logger.log(Level.INFO, "Working-{0}", ++iterations);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex){}
        }
    });
}

Neste exemplo de serviço sem estado, a contagem é armazenada em uma variável local. Mas como esse é um serviço sem monitoração de estado, o valor armazenado existe apenas para o ciclo de vida atual de sua instância de serviço. Quando o serviço é movido ou reiniciado, o valor é perdido.

Criar um serviço com monitoração de estado

O Service Fabric introduz um novo tipo de serviço com monitoração de estado. Um serviço com estado pode manter o estado de forma confiável dentro do próprio serviço, colocalizado com o código que o está usando. O estado é disponibilizado altamente pelo Service Fabric sem a necessidade de persistir o estado em um armazenamento externo.

Para converter um valor de contador de stateless em altamente disponível e persistente, mesmo quando o serviço é movido ou reiniciado, você precisa de um serviço com monitoração de estado.

No mesmo diretório que o aplicativo HelloWorld, você pode adicionar um novo serviço executando o yo azuresfjava:AddService comando. Escolha o "Reliable Stateful Service" para sua estrutura e nomeie o serviço "HelloWorldStateful".

Seu aplicativo agora deve ter dois serviços: o serviço sem estado HelloWorld e o serviço stateful HelloWorldStateful.

Um serviço com monitoração de estado tem os mesmos pontos de entrada que um serviço sem monitoração de estado. A principal diferença é a disponibilidade de um provedor de estado que pode armazenar o estado de forma confiável. O Service Fabric vem com uma implementação de provedor de estado chamada Coleções Confiáveis, que permite criar estruturas de dados replicadas por meio do Gerenciador de Estado Confiável. Um Serviço Confiável com monitoração de estado usa esse provedor de estado por padrão.

Abra HelloWorldStateful.java em HelloWorldStateful -> src, que contém o seguinte método RunAsync:

@Override
protected CompletableFuture<?> runAsync(CancellationToken cancellationToken) {
    Transaction tx = stateManager.createTransaction();
    return this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap").thenCompose((map) -> {
        return map.computeAsync(tx, "counter", (k, v) -> {
            if (v == null)
                return 1L;
            else
                return ++v;
            }, Duration.ofSeconds(4), cancellationToken)
                .thenCompose((r) -> tx.commitAsync())
                .whenComplete((r, e) -> {
            try {
                tx.close();
            } catch (Exception e) {
                logger.log(Level.SEVERE, e.getMessage());
            }
        });
    });
}

RunAsync

RunAsync() opera de forma semelhante em serviços com e sem estado. No entanto, em um serviço com monitoração de estado, a plataforma executa trabalho adicional em seu nome antes de ser executada RunAsync(). Esse trabalho pode incluir a garantia de que o Reliable State Manager e o Reliable Collections estejam prontos para uso.

Coleções confiáveis e o gestor de estado confiável

ReliableHashMap<String,Long> map = this.stateManager.<String, Long>getOrAddReliableHashMapAsync("myHashMap")

ReliableHashMap é uma implementação de dicionário que você pode usar para armazenar de forma confiável o estado no serviço. Com o Service Fabric e HashMaps confiáveis, você pode armazenar dados diretamente em seu serviço sem a necessidade de um armazenamento persistente externo. HashMaps confiáveis tornam seus dados altamente disponíveis. O Service Fabric faz isso criando e gerenciando várias réplicas do seu serviço para você. Ele também fornece uma API que abstrai as complexidades do gerenciamento dessas réplicas e suas transições de estado.

As Coleções Confiáveis podem armazenar qualquer tipo Java, incluindo seus tipos personalizados, com algumas ressalvas:

  • O Service Fabric torna seu estado altamente disponível replicando o estado entre nós, e o Reliable HashMap armazena seus dados no disco local em cada réplica. Isso significa que tudo o que é armazenado em Reliable HashMaps deve ser serializável.

  • Os objetos são replicados para alta disponibilidade quando você confirma transações em HashMaps confiáveis. Os objetos armazenados em HashMaps confiáveis são mantidos na memória local em seu serviço. Isso significa que você tem uma referência local ao objeto.

    É importante que você não mute instâncias locais desses objetos sem executar uma operação de atualização na coleção confiável em uma transação. Isso ocorre porque as alterações em instâncias locais de objetos não serão replicadas automaticamente. Você deve reinserir o objeto novamente no dicionário ou usar um dos métodos de atualização no dicionário.

O Reliable State Manager gerencia HashMaps confiáveis para você. Você pode pedir ao Gestor de Estado Confiável uma coleta confiável pelo nome a qualquer momento e em qualquer lugar em seu serviço. O Reliable State Manager garante que você receba uma referência de volta. Não recomendamos que você salve referências a instâncias de coleta confiáveis em variáveis ou propriedades de membros de classe. Cuidados especiais devem ser tomados para garantir que a referência seja definida para uma instância em todos os momentos do ciclo de vida do serviço. O Reliable State Manager lida com esse trabalho para você e é otimizado para visitas repetidas.

Operações transacionais e assíncronas

return map.computeAsync(tx, "counter", (k, v) -> {
    if (v == null)
        return 1L;
    else
        return ++v;
    }, Duration.ofSeconds(4), cancellationToken)
        .thenCompose((r) -> tx.commitAsync())
        .whenComplete((r, e) -> {
    try {
        tx.close();
    } catch (Exception e) {
        logger.log(Level.SEVERE, e.getMessage());
    }
});

As operações em HashMaps confiáveis são assíncronas. Isso ocorre porque as operações de gravação com Coleções Confiáveis executam operações de E/S para replicar e persistir dados no disco.

As operações HashMap confiáveis são transacionais, para que você possa manter o estado consistente em vários HashMaps e operações confiáveis. Por exemplo, você pode obter um item de trabalho de um Dicionário Confiável, executar uma operação nele e salvar o resultado em outro HashMap Confiável, tudo dentro de uma única transação. Isso é tratado como uma operação atômica e garante que toda a operação será bem-sucedida ou toda a operação será revertida. Se ocorrer um erro depois de retirar o item da fila, mas antes de salvar o resultado, toda a transação será revertida e o item permanecerá na fila para processamento.

Criar a aplicação

O andaime Yeoman inclui um script gradle para construir o aplicativo e scripts bash para implantar e remover o aplicativo. Para executar o aplicativo, primeiro compile o aplicativo com gradle:

$ gradle

Isso produz um pacote de aplicativo do Service Fabric que pode ser implantado usando a CLI do Service Fabric.

Implementar a aplicação

Depois de criada a aplicação, pode implementá-la no cluster local.

  1. Ligue ao cluster do Service Fabric local.

    sfctl cluster select --endpoint http://localhost:19080
    
  2. Execute o script de instalação fornecido no modelo para copiar o pacote de aplicação para o arquivo de imagens do cluster, registar o tipo de aplicação e criar uma instância da mesma.

    ./install.sh
    

A implementação da aplicação criada é igual à de qualquer outra aplicação do Service Fabric. Veja a documentação sobre como gerir uma aplicação do Service Fabric com a CLI do Service Fabric para obter instruções detalhadas.

Os parâmetros desses comandos encontram-se nos manifestos gerados dentro do pacote de aplicação.

Após a implementação da aplicação, abra um browser e navegue até Service Fabric Explorer em http://localhost:19080/Explorer. Em seguida, expanda o nó Aplicações e repare que há, agora, uma entrada para o tipo de aplicação e outra para a primeira instância desse tipo.

Importante

Para implantar o aplicativo em um cluster Linux seguro no Azure, você precisa configurar um certificado para validar seu aplicativo com o tempo de execução do Service Fabric. Isso permite que seus serviços de Serviços Confiáveis se comuniquem com as APIs de tempo de execução subjacentes do Service Fabric. Para saber mais, consulte Configurar um aplicativo de Serviços Confiáveis para ser executado em clusters Linux.

Próximos passos