Categories
Engenharia de software Webservices

Arquitetura “pull” ou “push”: qual escala mais?

Apesar do que possa parecer, esse post não é sobre git. 🙂

Conversando com o Peleteiro esses dias ele me deu uma idéia interessante. Ia ser o máximo se houvesse um post automático no meu Twitter toda vez que eu ganhasse um achievement no Xbox! Os achievements são como se fossem medalhas: na medida que você vai jogando os jogos e passando de fase ou conquistando coisas, você vai ganhando mais pontos e mais medalhas. Resumindo, é totalmente inútil mas bem legal.

Como nos últimos tempos andei fazendo uma porção de robôs de Twitter para fazer uma porção de coisas, na mesma hora pensei em fazer mais um deles, que ficaria dando requests na API do Xbox Live até que aparecesse um novo achievement e então ele faria o post.

Na mesma hora me bateu a mesma sensação de ineficiência que tive quando fiz os outros robôs. Pra fazer essas coisas eu tenho que ficar fazendo polling no serviço de “n” em “n” minutos para saber das atualizações… Apesar de ser eficiente nao é lá muito eficaz.

Vou fazer uma brincadeira para mostrar o tamanho do problema. Somando só os robôs que fiz para a Globo.com, eu faço diariamente cerca de 72.000 requests para o Twitter (cerca de 25 robôs x 2 requests por minuto x 60 minutos x 24 horas). Ok, nem todo mundo faz robôs de Twitter, eu sei, mas vamos supor que uma pessoa em cada 100.000 faz robôs que nem eu. Isso significaria que só eu e mais uma pessoa fazemos isso no Brasil inteiro (o que não deve ser verdade, mas vamos continuar assim fazendo a conta por baixo). Então considerando que a população mundial é de quase 7 bilhões de pessoas, temos cerca de 70.000 pessoas fazendo o mesmo em todo o mundo. Sendo assim, pela minha conta de padaria o Twitter recebe algo em torno de 5,04 bilhões de requisições por dia, 210 milhões de requisições por hora, quase 60.000 requisições por segundo (só de robôs)!!!

Ok, o número deve ser bem diferente disso. O fato é que com certeza é muito alto.

Eu faço polling no Twitter para buscar por usuários que falaram uma determinada palavra (usando o RSS da busca do Twitter) e então adicioná-los. Funciona assim: quando uma pessoa escreve alguma coisa que eu estou procurando, eu adiciono ela como amigo(a). Apesar dessa quantidade imensa de buscas e requests, um robô adiciona apenas na faixa de 25 usuários por dia. Se houvesse alguma forma de saber que algum usuário escreveu alguma palavra que eu busco, nesse meu cenário só seria necessário fazer algo em torno de 625 requests por dia (menos de 1% da quantidade que eu faço).

O que eu estou propondo aqui não é nada novo. Além dos mecanismos tradicionais de polling (fazer “pull” de arquivos de tempos em tempos), estes tipos de serviços deveriam disponibilizar um mecanismo para o servidor avisar o cliente que alguma informação que ele deseja está disponível (“push”).

Depois da apresentação “Beyond REST? Building data services with XMPP” que rolou na OSCON 2008, o XMPP entrou na moda como uma possível solução para esse problema. Nessa arquitetura, os clientes se “inscrevem” em um serviço de mensagens instantâneas e mantém uma conexão aberta com o servidor para que, quando determinada informação estiver disponível, ela seja enviada ao cliente ao invés dele ter que ficar fazendo “polling”. Isso reduz consideravelmente a carga de requests recebida pela aplicação, mas é mais difícil de escalar para uma quantidade muito grande de usuários.

Uma solução que surgiu na nossa conversa foi um “pingback ao contrário”. Seria algo como o webhook do Github. Quando o usuário fizer um GET em um recurso, ele poderia mandar um header com uma URL, e quando houvesse alguma atualização do recurso o servidor poderia fazer um GET para esta URL com o objetivo de informar ao cliente que há informações novas disponíveis. Essa solução escala mais do que a primeira, mas tem um lado negativo: não funciona se o cliente não tiver um IP real disponível. No meu caso, por exemplo, que tenho robôs rodando no meu desktop da Globo.com (que não tem IP real), não seria possível usar esse tipo de serviço.

Também dá pra fazer algumas soluções mais criativas usando coisas que a princípio parecem esquisitas mas podem fazer sentido. Por exemplo, um servidor de e-mail como o Postfix poderia fazer esse trabalho com o pé nas costas. Quando o cliente acessasse um recurso, ele poderia informar em um header um e-mail para ser notificado quando houvesse atualização. É uma solução bem fácil de escalar e de fácil implementação – tanto pelo lado do cliente quanto do servidor – apesar de não ser muito comum.

Pelo que eu andei lendo o pessoal do Twitter já pensou nesse problema e para resolvê-lo criaram o Firehose, que é uma solução baseada em XMPP. Como eu falei, o problema não termina por aí – eles terão vários problemas novos para escalar XMPP para uma grande quantidade de usuários.

Enfim, esse problema é bem interessante… Na próxima oportunidade que tiver vou tentar fazer uma prova de conceito de todas essas opções para ver no que dá.

E para aqueles que ainda não entenderam, talvez fique um pouco mais claro agora: escalabilidade é muito mais uma questão de arquitetura de software e infra-estrutura do que de linguagens e frameworks.