ZooKeeper
O Apache ZooKeeper é um serviço centralizado para resolver nomes, fornecer informação de configuração e sincronizar conjuntos de servidores distribuídos.
O seu objetivo é simplificar a gestão de serviços, com propagação fiável de alterações de configuração.
Mais informação:
ZooKeeper Documentation
Obtenção e Instalação
Obter
-
Apache ZooKeeper
(versão stable)
-
Obtenha a versão pré-compilada.
(Versão com binários incluidos)
- exemplo: apache-zookeeper-x.y.z-bin.tar.gz
Instalar
- Descompactar para pasta à escolha.
- exemplo: /home/user/zookeeper
Configurar
- Criar a pasta data dentro da pasta de instalação do ZooKeeper.
É nesta pasta que o ZooKeeper irá armazenar a informação.
- exemplo: /home/user/zookeeper/data
- Mudar o nome do ficheiro zookeeper/conf/zoo_sample.cfg para zoo.cfg
- Editar o ficheiro zoo.cfg, alterando as seguintes configurações:
- initLimit=10
- syncLimit=2
- dataDir=/home/user/zookeeper/data
- Repare no campo clientPort
este é o porto em que o ZooKeeper estará disponivel
Utilização
Vamos agora iniciar o servidor do ZooKeeper.
- Navegue até à pasta zookeeper/bin
- Corra o comando ./zkServer.sh start
- Deverá obter o seguinte output:
$ Starting zookeeper ... STARTED
O ZooKeeper está agora a correr.
Consola de interação com o servidor ZooKeeper
- Novamente na pasta zookeeper/bin, corra o comando ./zkCli.sh
- Este comando inicializa uma consola que permite interagir com o servidor ZooKeeper.
- Na consola correr o comando ls /
Este comando mostra os nós zNodes existentes na raiz (/) do ZooKeeper.
- Crie agora um novo nó com o comando create
- correr create /myFirstZnode myFirstData
- Verificar que o nó foi criado com sucesso.
- Verifique que o nó criado contem os dados
- correr o comando get /myFirstZnode
- verificar os dados devolvidos
- Vamos agora eliminar o nó criado
- correr o comand delete /myFirstNode
- verificar com ls /
- Para finalizar, correr quit para encerrar a consola do cliente.
- Para encerrar o servidor, corra o comando ./zkServer.sh stop
.
Utilização do ZooKeeper para registo de serviços
Os endereços -- servidor e porto -- de serviços gRPC devem ser descobertos de forma dinâmica, para evitar que fiquem fixos no código das aplicações.
Isto permite que o endereço mude sempre que necessário, sem recompilação dos clientes.
Permite também que existam diversas réplicas de servidores para contactar alternativamente.
As funcionalidades do ZooKeeper vão para além de um simples servidor de nomes.
Para tornar a utilização do ZooKeeper mais simples foi criada a biblioteca ZKNaming que oferece as operações rebind e lookup.
Esta biblioteca torna o registo e pesquisa de serviços mais sucinto.
O código fonte está disponível para consulta e modificação, caso sejam necessárias mais funcionalidades do ZooKeeper.
Mais informação:
ZKNaming JavaDoc
Exercício
O objetivo do exercício desta aula é usar a biblioteca ZKNaming para que o servidor grpc-hello (da aula anterior) se registe no ZooKeeper.
- Relembre o exemplo gRPC HelloWorld fornecido na aula anterior.
-
Descarregue e descomprima o código fonte da aplicação de exemplo com gRPC
-
Analise o código para relembrar a implementação.
-
Comece pelo módulo contract, e execute o comando:
mvn install
-
Abra uma consola e corra o servidor:
mvn compile exec:java
-
Abra outra consola e execute o cliente:
mvn compile exec:java
- Vamos agora obter a biblioteca ZKNaming e instalar no repositório local do Maven.
-
Obtenha o codigo da biblioteca zk-naming [atualizado]
-
Instalar o módulo no repositório Maven Local:
Atenção: Para correr os exemplos seguintes, é necessario que o servidor ZooKeeper já tenha sido iniciado: ./path/to/zookeeper/bin/zkServer.sh start
cd zk-naming
mvn install
- Uma vez instalado o módulo no repositório Maven local, a biblioteca pode ser usada como dependência em qualquer
pom.xml.
- Vamos agora adaptar o gRPC HelloWorld para uma aplicação cliente-servidor que utiliza a biblioteca ZKNaming para registar e procurar serviços distribuídos.
-
Observe o pom.xml do servidor HelloWorld:
- Adicione a dependência para a biblioteca ZKNaming:
...
<!-- ZK Naming -->
<dependency>
<groupId>pt.ulisboa.tecnico.sdis</groupId>
<artifactId>zk-naming</artifactId>
<version>1.0.2</version>
</dependency>
...
-
Confirme as dependências, fazendo:
mvn dependency:tree
- O servidor deverá receber como argumentos o endereço e porto do ZooKeeper, bem como o seu próprio host, port e nome, no formato:
- /HelloService
- o argumento nome será referênciado no resto do exercício como path
...
<properties>
<zoo.host>localhost</zoo.host>
<zoo.port>2181</zoo.port>
<server.host>localhost</server.host>
<server.port>8080</server.port>
<server.path>/HelloService</server.path>
...
...
<mainClass>${mainclass}</mainClass>
<arguments>
<argument>${zoo.host}</argument>
<argument>${zoo.port}</argument>
<argument>${server.host}</argument>
<argument>${server.port}</argument>
<argument>${server.path}</argument>
...
-
Altere o código do servidor, de modo a chamar a biblioteca zk-naming para registar o seu host e port.
- Adicionar as seguintes linhas de código ao servidor:
...
ZKNaming zkNaming = null;
try {
...
zkNaming = new ZKNaming(zooHost, zooPort);
// publish
zkNaming.rebind(path,host,port);
...
// start gRPC server
...
// await termination
...
} catch (Exception e) {
System.out.printf("Caught exception: %s%n", e);
} finally {
...
if (zkNaming != null) {
// remove
zkNaming.unbind(path,host,port);
}
...
}
- Atenção: o método unbind deve ser chamado no final do método main do servidor.
- Vamos alterar o pom.xml do cliente:
- Adicione a dependência para a bliblioteca zk-naming, no mesmo modo que fez para o servidor.
- Adicione ao pom.xml do cliente o endereço e porto do ZooKeeper, bem como o path do servidor.
Este tem que ser igual ao colocado no pom do servidor.
- Altere o código do cliente de modo a chamar a biblioteca zk-naming para procurar o servidor no ZooKeeper e obter o respectivo host e port.
- Adicionar as seguintes linhas de código ao cliente:
...
ZKNaming zkNaming = new ZKNaming(zooHost,zooPort);
...
// lookup
ZKRecord record = zkNaming.lookup(path);
String target = record.getURI();
final ManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build();
...
// make gRPC calls
...
- Repare que substituímos o argumento do método forTarget() pelo par host:port obtido através do ZooKeeper.
- Execute novamente o cliente e servidor:
- Mantenho o servidor ZooKeeper a correr (inicie se estiver parado)
- Corra mvn compile exec:java para o servidor e cliente
- Observe que o cliente comunica com o servidor, sem ter conhecimento explícito do host:port do mesmo
- Pode comprovar, alterando o porto do servidor e testando o cliente sem alterações na sua configuração