ZooKeeper
O Apache ZooKeeper é um servidor 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
Instalação do ZooKeeper
- Obter
- Ir à página do ZooKeeper e obter a versão stable mais recente, com binários incluidos (o ficheiro a obter segue o padrão: apache-zookeeper-x.y.z-bin.tar.gz)
- Instalar
- Descompactar para pasta à escolha, por exemplo, /home/user/zookeeper em Linux, ou C:\zookeeper em Windows
- Configurar
- Criar a pasta data dentro da pasta de instalação do ZooKeeper, seguindo o exemplo, /home/user/zookeeper/data em Linux, ou C:\zookeeper\data em Windows.
É nesta pasta que o ZooKeeper irá armazenar informação.
- Mudar o nome do ficheiro zookeeper/conf/zoo_sample.cfg para zoo.cfg
- Editar o ficheiro zoo.cfg, alterando a configuração dataDir para referenciar a pasta criada anteriormente.
Linux:
- dataDir=/home/user/zookeeper/data
Windows:
- dataDir=/C:/zookeeper/data
- As restantes configurações podem ser deixadas com os valores por omissão, nomeadamente o clientPort que indica o porto onde o ZooKeeper estará disponível.
Utilização
- Vamos agora iniciar o servidor ZooKeeper:
- Navegue até à pasta zookeeper/bin
- Corra o comando ./zkServer.sh start em Linux, ou apenas zkServer.cmd em Windows
- Deverão surgir mensagens de arranque na consola do servidor.
- O servidor ZooKeeper está agora a correr.
- Consola de interação com o servidor ZooKeeper:
- Novamente na pasta zookeeper/bin, corra o comando ./zkCli.sh em Linux, ou zkCli.cmd em Windows
- Este comando inicializa uma consola que permite interagir com o servidor ZooKeeper.
- Na consola do cliente Zookeeper, correr o comando: ls /
- Este comando mostra os nós (chamados zNodes), existentes na raiz (/) do ZooKeeper.
- Crie agora um novo nó com o comando create:
- Correr create /myNode myData
- Confirme que o nó foi criado com sucesso.
- Verifique que o nó criado contém os dados armazenados:
- Correr o comando get /myNode
- Verificar os dados devolvidos
- Vamos agora eliminar o nó criado:
- Correr o comand delete /myNode
- Verificar com ls /
- Para encerrar a consola do cliente, correr o comando quit.
- Para encerrar o servidor, corra o comando ./zkServer.sh stop em Linux, ou faça Ctrl+C na consola caso esteja em Windows.
Utilização do ZooKeeper para registo de serviços gRPC
Os endereços de serviços gRPC -- servidor e porto -- 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.
A interface de clientes ZooKeeper está orientada à monitorização ativa de configurações, o que torna o código bastante verboso.
Para simplificar a utilização do ZooKeeper foi criada a biblioteca ZKNaming que oferece as operações bind (registo), lookup e listRecords (pesquisas).
Esta biblioteca torna o código de registo e pesquisa de serviços muito mais sucinto.
O código fonte da biblioteca está disponível para consulta e pode ser modificado, caso seja necessário expor 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 silo-server se registe no ZooKeeper.
- Vamos obter a biblioteca ZKNaming e instalar no repositório local do Maven.
-
Obtenha o codigo da biblioteca zk-naming
-
Instalar o módulo no repositório Maven Local:
Atenção: é necessario que o servidor ZooKeeper já tenha sido iniciado.
- 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.
- Relembre o exemplo de gRPC estudado na aula de gRPC
- Faça Clone ou Download do branch zk código fonte do exemplo gRPC
- Execute o exemplo seguindo as instruções README.md de cada módulo.
- Experimente alterar o porto do servidor no pom.xml e executar novamente ambos os módulos, sem alterar o cliente.
- Familiarize-se com o código e responda às seguintes questões:
- Onde está o registo do servidor no serviço de nomes?
- Como é que o cliente obtém a localização do servidor?
- Pode também comparar as diferenças entre o branch e o exemplo de base
- Vamos agora adaptar o silo 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 silo-server:
- Adicione a dependência para a biblioteca ZKNaming instalada anteriormente:
...
<!-- ZK Naming -->
<dependency>
<groupId>pt.ulisboa.tecnico.sdis</groupId>
<artifactId>zk-naming</artifactId>
<version>1.0.3</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 /grpc/sauron/silo/instance (este nome será referenciado no resto do exercício como path):
...
<properties>
<zoo.host>localhost</zoo.host>
<zoo.port>2181</zoo.port>
<!-- instance number -->
<instance>1</instance>
<server.host>localhost</server.host>
<server.port>808${instance}</server.port>
<server.path>/grpc/sauron/silo/${instance}</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>
...
Note a definição da propriedade instance, usada para representar o número de instância do servidor.
-
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
...
} 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 agora alterar os clientes do silo:
- Vamos começar por atualizar os pom.xml:
- Adicione a dependência para a biblioteca zk-naming no silo-client, no mesmo modo que fez para o servidor.
- Adicione ao pom.xml do eye o endereço e porto do ZooKeeper, bem como o path do servidor.
Este tem de ser igual ao colocado no pom do servidor.
- Proceda de igual modo para o spotter
- Altere o código do SiloFrontend 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();
...
- Repare que substituímos o argumento do método forTarget() pelo par host:port obtido através do ZooKeeper.
- Execute os clientes e servidor:
- Mantenha o servidor ZooKeeper a correr (inicie se estiver parado)
- Corra mvn compile exec:java para o servidor e clientes
- Experimente correr vários comandos no eye e no spotter
- Observe que os clientes comunicam 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
-
E se tivéssemos várias réplicas? Como poderia encontrá-las dinamicamente?
- Estude a operação listRecords