Pesquisar este blog

terça-feira, 9 de dezembro de 2014

Apache ZooKeeper Notificações e Watches

Apache ZooKeeper Notificações e Watches

Como o ZooKeeper é tipicamente acessado como um serviço remoto, o acesso aos znodes todas as vezes que um cliente precisa saber algum dado do znode, tornaria muito custosa a manutenção, aumentaria muito o tempo de latência (tempo gasto entre a chamada e a resposta) e ainda iria implicar em mais operações durante a instalação do ZooKeeper.

Watches (Notificador)

O ZooKeeper pode te notificar quando um znode, ou algum de seus filhos, forem modificados. Para registrar um notificador no seu znode você deve utilizar os comandos get ou stat para acessar o conteúdo atual ou os metadados daquele znode, e com isso passar um novo parâmetro registrando o notificador. Para registrar um notificador em um znode filho , voce deve passar o mesmo parametro que foi usado ao pegar o filho com o comando ls.
create /demo/notifique data
Created /notifique
get /demo/notifique true
data
Ao modificar este znode, ou algum filho dele, você verá a seguinte mensagem no console:
 WATCHER 
WatchedEvent state:SyncConnected type:NodeDataChanged path:/notifique
Atente ao detalhe de que um notificador só é disparado uma vez, com isso, se você quiser  ser notificado nas próximas notificações você deverá resetar o notificador a cada modificação. O notificador permite que você utilize o ZooKeeper para implementar um sistema assíncrono, baseado em eventos, e notificar os nós quando a sua cópia local dos dados estiver ultrapassada/desatualizada.

sábado, 29 de novembro de 2014

Apache ZooKeeper diferentes modos de Nós

Nós Sequenciais

Além dos nós comuns do ZooKeeper, existem os nós sequenciais e para criá-los você deve usar os argumentos -s durante a criação de um nó. Também é possíve alterar o tipo de um zode para estes tipos. Um nó sequencial será criado com um sufixo numérico adicionado ao nome e o ZooKeeper garante que nunca existirão dois nós com a mesma numeração, mesmo que eles sejam criados em paralelo.
[zk: localhost:2181(CONNECTED) 9] create -s /exemplo/sequencial um
Created /exemplo/sequencial0000000002
[zk: localhost:2181(CONNECTED) 10] create -s /exemplo/sequential dois
Created /exemplo/sequencial0000000003
[zk: localhost:2181(CONNECTED) 11] ls /exemplo
[sequencial0000000003, sequencial0000000002]
[zk: localhost:2181(CONNECTED) 12] get /exemplo/sequencial0000000002

Os znodes sequenciais são bem úteis para criar znodes com nomes únicos e para saber qual foi a sequencia de criação dos znodes.

Tipos de nós

Existem dois tipos de nós, os efêmeros e os persistentes, os persistentes são aqueles que são armazenados e somente serão apagados quando chamamos o comando delete.

Nós Persistentes

Os nós persistentes são utilizados para armazenar dados que são utilizados pelo aplicativo e precisam ser preservados mesmo quando o aplicativo é reiniciado ou o seu criador não faz mais parte do sistema. Por exemplo um nó master percisa controlar quais trabalhos foram enviados para quais clientes, e estes dados precisam ser recuperados pelo novo master em caso de uma falha do master. Com isso estes dados são armazenados em znodes persistentes.

Nós Efêmeros

Os nós efêmeros podem ser criados através do argumento -e, usado no comando de criação. Um nó efêmero será apagado se o cliente que criou aquele nó parar. Os nós efêmeros são úteis para armazenar aspectos da aplicação, por exemplo dados da sessão.
Um belo exemplo de utilização de um znode efêmero é nos masters do zookeeper, eles criam um znode efêmero master, e enquanto este znode estiver lá, o master está funcionando. Com isso é possível detectar quando o master não está mais disponível e com isso criar uma nova instância dele. Isto também poderá ser utilizado para os clientes e isso torna bem mais simples a construção de um sistema distribuído com controle de falhas.
Um nó efêmero pode ser deletado em dois casos, quando a sessão expira ou é explicitamente fechada, ou quando o comando de delete é executado.
Como os nós efêmeros são apagados assim que a sessão expira, não é possível criar filhos nestes nós.

segunda-feira, 24 de novembro de 2014

Exercícios WebServices REST e Twitter Stream


  1. Crie um webservice REST no playframework usando o caminho  /professor/_nome (onde nome é o nome do professor) como rota. Este webservice deverá retornar uma mensagem de Olá _nome.
  2. Crie um webservice REST no playframework usando o caminho /professor/lista como rota. Este webservice deverá retornar uma lista de Professor, onde Professor é o seguinte objeto.
    public class Professor {
       private String nome;
       private Integer id;

       public Professor(String nome, Integer id){
          this.nome = nome;
          this.id = id;
       }
       // getters e setters, hashCode e equals...

    }
  3. Explique em detalhes as caracteristicas de um webservice REST
  4. Explique em detalhes as caracteristicas de um servico stream
  5. Implemente um webservice no play que retorne múltiplas mensagens do tipo Chunk
  6. Implemente um webservice no play que retorne múltiplas mensagens do tipo Comet.

sábado, 22 de novembro de 2014

Operações básicas com o Cliente do Apache ZooKeeper

O Apache ZooKeeper vem com um console de uso interativo que pode ser usado como cliente do apache do seu servidor, o que facilita muito os testes do serviço. Em um aplicativo real você deve utilizar uma biblioteca, o que está disponível em Python, Java, C e Perl.
Neste post vamos brincar um pouco com o console para demonstrar o seu funcionamento.
Para executar o cliente do ZooKeeper basta que você execute o programa zookeeper-client. O prompt inicial poderá ficar escondido devido aos logs de execução, então basta que você aperte o ENTER para ver o console. Tente digitar ls /, que serve para listar os arquivos do servidor ou help para descobrir os comandos disponíveis neste console.
Isto é similar a um shell de comando do sistema de arquivos de um Sistema baseado no Unix. O ZooKeeper armazena dados em uma hierarquia de znodes (nós z). Nesta hierarquia de nós cada um pode conter dados (por exemplo um arquivo) e também conter um filho (como um diretório), no ZooKeeper estes nós foram construidos para trabalhar com uma pequena quantidade de dados, o limite padrão é 1MB por nó.

Criando ZNodes no ZooKeeper

Para armazenar dados no ZooKeeper devermos criar um znode, o que é bem simples, como exemplo vamos criar um znode pai para armazenar um znode filho de exemplo.
[zk: localhost:2181(CONNECTED) 2] create /pai ''
Created /pai
[zk: localhost:2181(CONNECTED) 3] create /pai/exemplo 'ExemploDSD!'
Created /pai/exemplo
Lembrando que não há nenhuma relação entre o nome do znode e o seu conteúdo. Como foi mostrado acima, o comando create é responsável por criar um znode. Eles funcionam de uma maneira bem parecida com os diretórios que temos nos sistemas de arquivos, quando você criar um subdiretório, neste caso um znode filho, basta passar o nome do caminho completo dele, no caso acima pai que será o znode pai, e pai/exemplo será criado um znode exemplo dentro do znode pai.


Armazenamento de dados com o ZooKeeper (Leitura e Alteração)

Para ler o conteúdo de um nó no ZooKeeper deve-se utilizar o comando Get, ao digitar este comando o conteúdo do znode será impresso na primeira linha do console, como mostrado abaixo:






[zk: localhost:2181(CONNECTED) 4] get /pai/exemplo
'ExemploDSD!'
dataVersion = 0
 Também é possível alterarmos o conteúdo de um nó após a sua criação, note que após esta alteração o conteúdo da dataVersion será modificado, pois este atribudo é quem determina a versão do dado contido naquele nó. Para alterarmos o valor de um znode deveremos utilizar o comando set.
[zk: localhost:2181(CONNECTED) 5] set /pai/exemplo 'Exame!'
dataVersion = 1
[zk: localhost:2181(CONNECTED) 6] get /pai/exemplo
'Exame!'
dataVersion = 1

Apagando dados no ZooKeeper

Para apagarmos dados do ZooKeeper deveremos utilizar o comando delete. Assim como nos diretórios, znodes pais não podem ser apagados, a não ser que todos os seus filhos sejam apagados antes.
[zk: localhost:2181(CONNECTED) 7] delete /pai/exemplo
[zk: localhost:2181(CONNECTED) 8] ls /pai
[]

quinta-feira, 20 de novembro de 2014

Instalação do Apache Zookeeper

Antes de iniciar a instalação veja os requisitos do sistema para a instalação do Apache ZooKeeper.

Servidor Clusterizado

Para ter um serviço confiável do ZooKeeper você deve realizar um deploy em um cluster conhecido como conjunto. Enquanto a maioria dos servidores do conjunto estiver de pé, o serviço estará disponível. Isto acontece pois o ZooKeeper necessita de uma maioria, e por isso é recomendável ter um número impar de servidores. Por exemplo se você tiver 4 máquinas, apenas uma poderá apresentar falha, pois se duas falharem, as outras duas que sobram não representam uma maioria, porém com 5 máquinas, mesmo com 2 falhas as três restantes ainda representam a maioria e com isso o serviço permanece de pé.
Abaixo seguem os passos para se configurar um servidor que será parte de um conjunto. Estes passos deverão ser executados em todas as cópias que fazem parte do seu cluster de conjunto.
  1. Instale o JDK do Java, você pode utilizar um instalador nativo pro seu sistema Operacional, ou fazer o download do JDK aqui.
  2. Configure o Heap Size do Java, isto é muito importante para evitar o swap, o que degrada seriamente a performance do servidor. Para saber qual é o valor correto a ser utilizado no heap do java utilize testes de carga e tenha certeza que você ainda possui uma margem de segurança, ou seja, memória livre, antes de entrar no swap.
  3. Instale o servidor ZooKeeper, que pode ser encontrado aqui
  4. Crie um arquivo de configuração, que pode ter qualquer nome, use o exemplo a seguir como um ponto de começo:
    tickTime=2000
    dataDir=/var/lib/zookeeper/
    clientPort=2181
    initLimit=5
    syncLimit=2
    server.1=zoo1:2888:3888
    server.2=zoo2:2888:3888
    server.3=zoo3:2888:3888
    Para saber mais detalhes dos parâmetros de configuração você pode consultar a página oficial de ajuda do ZooKeeper , abaixo o básico é explicado:
    Cada máquina de um cluster deverá conhecer todas as outras que fazem parte do conjunto. Esta configuração é realizada através das várias linhas no arquivo de configuração server.id=host:port:port. Cada servidor possui um identificador, que deve ser configurado em um arquivo chamado myid, um para cada servidor, que deverá ficar dentro do dataDir definido na sua configuração
  5. O arquivo myid deve conter uma simples linha com apenas um parâmetro, o nome do servidor. Que no caso do servidor.1 será apenas o valor "1" e nada mais. Este identificador deverá ser único para cada servidor dentro do cluster e o seu valor deverá ser entre 1 e 255.
  6.  Assim que o arquivo de configuração estiver estiver configurado você poderá inicializar o seu servidor do ZooKeeper com o comando:
    $ java -cp zookeeper.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:lib/log4j-1.2.15.jar:conf \ org.apache.zookeeper.server.quorum.QuorumPeerMain zoo.cfg
    QuorumPeerMain inicializa o servidor ZooKeeper, veja o script zkServer.sh contido dentro do diretório bin para maiores detalhes sobre como inicializar um servidor.
  7. O teste do seu servidor poderá ser realizado através de um comando:
    1. Java
      $ java -cp zookeeper.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:lib/log4j-1.2.15.jar:conf:src/java/lib/jline-0.9.94.jar \ org.apache.zookeeper.ZooKeeperMain -server 127.0.0.1:2181
    Ao rodar este comando você irá se conectar em um shell onde é possível executar operações simples. Para se conectar no servidor ZooKeeper com um cliente MultiThread você deve executar o seguinte comando:
$ cli_mt 127.0.0.1:2181
    assim que você se conectar no servidor você deverá ver alguma coisa como:
Connecting to localhost:2181
log4j:WARN No appenders could be found for logger (org.apache.zookeeper.ZooKeeper).
log4j:WARN Please initialize the log4j system properly.
Welcome to ZooKeeper!
JLine support is enabled
[zkshell: 0]
    Agora que você está conectado no servidor, já pode executar algumas operações básicas.

Servidor Simples e Setup de Desenvolvedor

Para configurar um servidor simples, ou um ambiente de desenvolvedor você deve executar os mesmos passos acima, a única diferença será no arquivo de configuração, que aqui será:
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181

Para inicializar o servidor basta executar o seguinte comando:
bin/zkServer.sh start

Requisitos de Sistema para a Instalação do Apache ZooKeeper

Plataformas Suportadas

  • GNU/Linux é suportado como uma plataforma de produção e desenvolvimento tanto como servidor quanto como cliente.
  • Sun Solaris é suportado como uma plataforma de produção e desenvolvimento tanto como servidor quanto como cliente.
  • FreeBSD é suportado como uma plataforma de produção e desenvolvimento apenas como cliente. O suporte para o seletor de Java NIO na plataforma de FreeBSD JVM está quebrado.
  • Win32 é suportado apenas como uma plataforma de desenvolvimento tanto como servidor quanto como cliente.
  • MacOSX é suportado apenas como uma plataforma de desenvolvimento tanto como servidor quanto como cliente.
O ZooKeeper roda sobre a máquina virtual Java versão maior que 6. Para rodá-lo precisamos de um conjunto de servidores, no mínimo três instancias. É recomendado que estes servidores rodem em máquians diferentes.
No Yahoo o Zookeeper roda em máquinas RedHat, Dual Core, com 2Gb de RAM e 80Gb de armazenamento.

quinta-feira, 13 de novembro de 2014

Apache Zookeeper como o coordenador do seu Sistema Distribuído

Apache Zookeeper como o coordenador do seu Sistema Distribuído

Nesta série de artigos será explicado como utilizar o Apache ZooKeeper como coordenador do seu sistema distribuído.

Índice:

Introdução ao Apache Zookeeper

Introdução ao Apache Zookeeper

Da mesma forma que sabemos que não deve-se sair implementando uma biblioteca de criptografia ou mesmo o seu servidor web, ao invés de utilizar um algorítmo já implementado e muito bem revisto pela comunidade, ou empresas. Implementar o seu próprio protocolo de coordenação de um sistema distribuído vai dar muito trabalho, ser muito complicado e provavelmente causar muita frustração.
Estes protocolos são extremamente complexos, a arquitetura de um sistema distribuído não é um problema simples de se resolver, como já vimos nestes posts, existem os problemas de concorrência, deadlock e consistência. Estes problemas são o que o Apache Zoopkeeper te ajuda a resolver. Ele é um sistema de coordenação que te ajuda a cconstruir um sistema distribuído corretamente.
O Apache Zookeper já resolve todos estes tipos de problemas, sem que você tenha que reinventar a roda. O Zookeeper já é utilizado em grandes projetos como o Apache HBase, o HDFS e o Apache Hadoop.
O HBase é um sistema de armazenamento de dados de BigData que provê acesso randômico e em tempo real de leitura e escrita a estruturas com bilhôes de linhas e milhôes de colunas.
O HDFS (Hadoop Distributed File System) é um sistema de arquivos distribuídos que foi desenvolvido para ser utilizado em computadores baratos, com grande tolerância a falhas, ele provê uma grande velocidade de acesso aos lados e é utilizado por aplicações que possuem uma quantidade de dados muito grande.
O Apache Hadoop é um framework que provê processamento de dados, desde um sistema que possui apenas um servidor e pode escalar até milhares de servidores, onde cada um deles provê um processamento local e um armazenamento de dados local. Ao invés de confiar em sistemas de hardware confiáveis para prover tolerância a falhas, o hadoop consegue detectar e contornar estas falhas por software provendo um serviço de alta disponibilidade em um cluster de computadores.

Como o ZooKeeper funciona

O Zookeeper roda em um cluster de servidores chamados de conjunto, os quais compartilham o estado dos seus dados entre si. Cada atualização de dados só é considerada uma alteração de sucesso, depois que metade das máquinas deste conjunto completa esta atualização. Um lider será eleito pelo conjunto e se duas atualizações conflitantes são enviadas para o conjunto, aquela que for processada pelo lider primeiro será a que prevalecerá, a outra irá falhar.
Nele é possível configurar um parâmetro de timeout de conexão, e o Zookeeper cuidará de desconectar qualquer servidor que fique desconectado por um tempo maior ou igual ao parâmetro de timeout. Enquanto houver mais da metade do conjunto online, o Zookeeper conseguirá manter o sistema consistente. Assim que um servidor que ficou offline voltar a se conectar ao Zookeeper ele irá resincronizar todos os seus dados.
É recomendado ter um número impar de servidores, por exemplo, se você tiver 5 servidores e um deles estiver em manutenção, você ainda consegue trabalhar se algum outro servidor se desconectar, porém se três servidores estiverem desconectado o seu serviço ficará indisponível. Quanto mais servidores você adiciona ao seu sistema, mais tolerante a falhas ele ficará, porém a escrita de dados se tornará mais lenta. Trabalhando em um sistema com seis servidores, eles ficarão desconectados caso três servidores fiquem offline.

sábado, 8 de novembro de 2014

Acessando o Twitter Stream via PlayFramework

Introdução

Atualmente existem diversas ferramentas que distribuem conteúdos via streaming, o Twitter é uma delas. 
Streaming é um tipo de transmissão de dados onde o tamanho dos dados a serem transmitidos não é conhecido, e em alguns casos ele é contínuo.
Ao desenvolver um sistema de webservice rest, como mostrado aqui e aqui, as respostas as chamadas dos webservices contém um parâmetro chamado Content-Length definido no header da resposta. Este parâmetro define o tamanho do conteúdo daquela resposta. 
Isso não é feito explicitamente, como foi mostrado nos posts, mas o framework de MVC faz a inclusão deste parâmetro automaticamente.
Esta definição funciona muito bem nos casos onde sabe-se o tamanho exato da resposta, porém como deve-se lidar com casos em que você não sabe o tamanho da resposta? 
Uma das formas de se resolver este problema é usar uma resposta fragmentada (Chunked responses).

Resposta Fragmentada (Chunked response)

A grande vantagem deste tipo de resposta é que pode-se disponibilizar um conteúdo para o cliente, assim que ele fica disponível ao servidor de web. Uma desvantagem a ser citada é que não é possível calcular quanto daquela mensagem já foi carregada, uma vez que não se sabe o tamanho total dela.
O modo mais fácil de utilizar este tipo de resposta no playframework, no caso de haver um inputstream dinâmico é:
public static Result indice() {
  InputStream is = getInputStreamDinamico();
  return ok(is);
}
Uma instância de um InputStream pode ser utilizado como resposta a uma chamada, conforme realizado pelo ok(is).
A outra maneira de usar as respostas fragmentadas é implementar um builder de respostas fragmentadas.

public static indice() {
  // Prepare um stream de texto fragmentado
  Chunks<String> chunks = new StringChunks() {
    
    // Chamado quando o stream estiver pronto
    public void onReady(Chunks.Out<String> out) {
      metodoRegistroOut(out);
    }
    
  }
  
  // Serve este stream como um HTTP 200 OK
  ok(chunks);
}
public void metodoRegistroOut(Chunks.Out<String> out) {   out.write("Olá");                                    out.write("dsd");                             out.write("mensagem");                                              out.close();
} 
Neste caso cria-se um chunk, tipado como String usando o java Generics, este chunk tem um método onReady que é chamado assim que o stream estiver pronto para ser utilizado, no exemplo acima este método irá registrar a saída (out) em um outro método que irá enviar algumas mensagens para o cliente como resposta.

Sockets Comet

Os comets são uma outra forma de se particionar as mensagens enviadas aos clientes. Os sockets comet são mensagens chunks de texto html que contem apenas elementos <script>. Em cada parte da mensagem será enviado ao cliente um elemento <script> que ao ser executado pelo browser pode chamar uma função javascript e com isso conseguimos enviar eventos ao vivo para os clientes.
A criação de comets é bem simples, como mostrado abaixo:
public static Result index() {
  // Prepara a stream de uma resposta particionada
  Chunks<String> chunks = new StringChunks() {
    
    // Método chamado assim que a resposta particionada estiver        // pronta
    public void onReady(Chunks.Out<String> out) {
      out.write("<script>console.log('Olá')</script>");
      out.write("<script>console.log('dsd')</script>");
      out.write("<script>console.log('mensagem')</script>");
      out.close();
    }
    
  }
  
  response().setContentType("text/html");
  
  ok(chunks);
}
Até agora nada muito diferente do que já havia sido exposto na seção de mensagens particionadas, a não ser pelas tags <scripts> nas respostas enviadas.
A grande vantagem de enviar scripts é poder iteragir e executar operações no cliente em forma de javascript. Coisas como alterar a cor do fundo da tela, criar um botão, realizar chamadas a serviços de dados, ou qualquer lógica que o desenvolvedor ache necessário.

Biblioteca play.libs.Comet

Esta biblioteca server para ajudar no envio de mensagens do tipo comet aos clientes:
public static Result indice() {
  Comet comet = new Comet("console.log") {
    public void onConnected() {
      sendMessage("Olá");
      sendMessage("dsd");
      sendMessage("mensagem");
      close();
    }
  };
  
  ok(comet);
}
O código acima faz as mesmas operações realizadas pelo código mostrado na seção de comet, a grande diferença é que com ela podemos mandar mensagens tanto strings, quanto objetos em JSON.