Labs SD >

Invocação de procedimentos remotos com gRPC

Objectivos da semana

Materiais de apoio à aula

 

Exercício a resolver até ao fim da aula

Neste exercício iremos transformar uma implementação do Jogo do Galo (Tic Tac Toe) numa aplicação distribuída utilizando o gRPC.

Tic Tac Toe
  1. Jogo do Galo/Tic Tac Toe
    1. Descarregue e descomprima o código fonte da implementação do jogo ZIP.
    2. Analise o código do jogo de forma a compreender a implementação.
    3. Compile e execute o código com o comando:
      mvn compile exec:java

  2. Pretende-se que a nova versão do programa seja dividida em dois processos: servidor e cliente, através do gRPC.
    Vamos começar por estudar a tecnologia gRPC.
    1. Descarregue e descomprima o código fonte da aplicação de exemplo com gRPC ZIP.
    2. Veja como o programa está estruturado em três módulos: contract, server e client.
      Cada módulo tem um POM próprio.
    3. Execute o exemplo seguindo as instruções README.md de cada módulo.
    4. Comece pelo módulo contract, executando o comando:
      mvn install
      Este comando vai passar pela etapa generate-sources, que vai invocar o protoc, o compilador de protocol buffers que vai gerar código Java para lidar com os tipos de dados descritos no ficheiro .proto.
      Familiarize-se com o código e tente responder às seguintes questões:
      1. Onde estão definidas as mensagens trocadas entre o cliente e o servidor?
      2. Onde estão definidas as operações remotas no servidor?
      3. Onde estão os ficheiros gerados pelo compilador de Protocol Buffers?
      4. Onde são feitas as invocações remotas no cliente?
      5. As invocações remotas são síncronas (bloqueantes) ou assíncronas?
    5. Abra uma consola e corra o servidor:
      mvn compile exec:java
    6. Abra uma outra consola e execute o cliente:
      mvn compile exec:java
      Depois de ver o Hello World a funcionar corretamente no seu computador, avance para o passo seguinte.

  3. Vamos agora transformar o Jogo do Galo numa aplicação cliente-servidor com gRPC organizada em três módulos, à semelhança do exemplo.
    O contrato irá definir a interface remota, com detalhes sobre as mensagens a trocar. O servidor irá manter o estado do jogo (tabuleiro). O cliente irá ter a interface utilizador na consola.

    1. Crie uma estrutura de pastas para o jogo distribuído ttt-grpc: contract, server e client. Adapte o POM de cada módulo a partir do exemplo, editando os nomes dos artefactos e restantes configurações.

    2. Baseando-se no módulo contract da aplicação de exemplo, crie um ficheiro .proto com a definição das mensagens e procedimentos necessários para as invocações remotas das funções já definidas (play, checkWinner e currentBoard). Sugestão: consulte a documentação dos Protocol Buffers.
      1. Declare todas as mensagens de pedido e resposta para cada operação do jogo. Note que algumas mensagens podem ser vazias, mas devem ser declaradas na mesma.
      2. Cada campo deve ter uma etiqueta numérica única.
      3. Declare um serviço com as definições das operações necessárias, usando as mensagens definidas.
      4. Instale o módulo com o comando mvn install. Analise o código Java gerado na pasta target/generated-sources/.
        1. Onde estão definidas as mensagens?
        2. E as operações?

    3. Baseando-se no módulo server da aplicação de exemplo, crie um novo projeto para conter a implementação do servidor.
      1. Confirme que o módulo contrato é uma dependência do projeto.
      2. Crie uma classe que implemente as operações remotas declaradas no contrato, utilizando a classe TTT (que implementa a lógica do jogo) definida no código base. Esta nova classe deve estender a classe do serviço definido no contrato e fazer override das operações declaradas no contrato.
        Exemplo de um método:
        	public class TTTServiceImpl extends TTTGrpc.TTTImplBase {
        		private TTT ttt = new TTT();
        
        		@Override
        		public void currentBoard(CurrentBoardRequest request, StreamObserver<CurrentBoardResponse> responseObserver) {
        			CurrentBoardResponse response = CurrentBoardResponse.newBuilder().setBoard(ttt.currentBoard()).build();
        			responseObserver.onNext(response);
        			responseObserver.onCompleted();
        		}
        	
        
      3. Crie uma classe que contenha a função main e que inicia um servidor numa porta que recebe como argumento, instanciando a classe criada na alínea anterior.
      4. Tenha em conta que o acesso a variáveis partilhadas tem que ser sincronizado.
        1. Porque é que esta sincronização é necessária?
        2. Onde é que há possibilidade de concorrência?

    4. Por fim, com base no módulo client da aplicação de exemplo, crie um projeto Maven para conter a implementação do cliente.
      1. Confirme que o módulo contrato é uma dependência do projeto.
      2. Crie uma classe que instancia um stub do serviço TTT (através de um endereço e porta recebidos como argumentos).
      3. Usando o código do ficheiro Game.java do código base, adicione métodos para jogar o jogo remotamente através do stub instanciado.
        Exemplo de adaptação:
        	// before the change: local call
        	winner = ttt.checkWinner();
        
        	// after the change: remote call
        	winner = stub.checkWinner(CheckWinnerRequest.getDefaultInstance()).getResult();
        				
    5. Lance o servidor e experimente jogar remotamente através do cliente construído.

    O resto do enunciado será entregue na aula.
    O objectivo será estender a solução resultante do enunciado acima com mais procedimentos remotos ou modificar alguns dos seus procedimentos actuais.

Entrega da solução

Fénix, Avaliação, Projetos, mini Exercício 1 - gRPC

A solução completa deverá ser submetida no Fénix antes do fim da sua aula de laboratório.
Trabalhos submetidos depois da hora de fim da aula não serão considerados.

Ter atenção ao seguinte:


© Docentes de Sistemas Distribuídos, Dep. Eng. Informática, Técnico Lisboa