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. Relembre o Jogo do Galo/Tic Tac Toe utilizado no laboratório anterior.
    1. Faça Clone ou Download do código fonte do jogo local GitHub
    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 da aplicação seja dividida em dois processos: servidor e cliente, através do gRPC.
    Vamos começar por estudar a tecnologia gRPC.
    1. Faça Clone ou Download do código fonte do exemplo gRPC GitHub
    2. Veja como a aplicação está estruturada 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 responda à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. Faça Clone ou Download do código inicial do exercício GitHub

    2. Baseando-se no módulo contract da aplicação de exemplo, modifique o ficheiro .proto com a definição das mensagens e procedimentos necessários para as invocações remotas das operações currentBoard, play e checkWinner.
      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, modifique o código inicial do módulo server.
      1. Confirme que o módulo contract é uma dependência do projeto.
      2. Modifique a classe TTTServiceImpl de forma a implementar as operações remotas declaradas no contrato, utilizando a classe TTTGame (que implementa a lógica do jogo) definida no código base. A classe de implementação do serviço estende a classe do serviço definido no contrato e faz override das operações declaradas no contrato.

        Exemplo de um método:
        public class TTTServiceImpl extends TTTGrpc.TTTImplBase {
        	private TTTGame ttt = new TTTGame();
        
        	@Override
        	public void currentBoard(CurrentBoardRequest request, StreamObserver<CurrentBoardResponse> responseObserver) {
        		CurrentBoardResponse response = CurrentBoardResponse.newBuilder().setBoard(ttt.currentBoard()).build();
        		responseObserver.onNext(response);
        		responseObserver.onCompleted();
        	}
        								
      3. Confirme que a classe TTTServer inicia um servidor numa porta que recebe como argumento, instanciando a classe de implementação do serviço.
      4. Tenha em conta que o acesso a variáveis partilhadas tem de ser sincronizado.
        1. Porque é que esta sincronização é necessária?
        2. Onde é que há possibilidade de concorrência?
      5. Lance o servidor:
        mvn compile exec:java

    4. Por fim, complete o código do módulo client.
      1. Confirme que o módulo contract é uma dependência do projeto.
      2. Confirme que a classe TTTClient instancia um stub do serviço TTT (através de um endereço e porta recebidos como argumentos).
      3. Adicione as chamadas remotas às operações play e checkWinner que estão em falta.

        Exemplo de chamada local:
        winner = ttt.checkWinner();
        								
        Exemplo de chamada remota correspondente:
        winner = stub.checkWinner(CheckWinnerRequest.getDefaultInstance()).getResult();
        								
    5. Experimente jogar remotamente através do cliente construído:
      mvn compile exec:java
  4. 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 2 - 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