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.

Categories
Internet Webservices

Web programável

Uma das coisas mais legais que surgiu nesses últimos tempos foi a web programável (a.k.a. programmable web).

A web programável não é uma tecnologia mas sim um conceito. Tecnologicamente falando não há nenhuma novidade. Todas as tecnologias que fazem parte da web programável já estão aí há um tempão: JavaScript, XML, webservices, HTTP, RSS, Atom… O grande barato disso é a idéia, que consiste em os sites disponibilizarem além de suas tradicionais interfaces web HTML, uma interface que possibilite que programas utilizem os serviços que o site oferece.

Isso possibilita por exemplo que você coloque um mapa do Google Maps no seu site, que você tenha um site de comércio eletrônico inteiro usando a infraestrutura de pagamento e shopping cart do PayPal, que você faça um sistema de backup armazenando os seus arquivos no sistema de arquivos S3 do Amazon, que você codifique vídeos em vários formatos utilizando a HeyWatch API e por aí vai… O limite é a imaginação.

Vários grandes players do mercado já disponibilizam suas APIs públicas como o eBay, Google, Yahoo, Skype, PayPal, Amazon, além de muitos outros.

O site ProgrammableWeb é um lugar legal para acompanhar as últimas APIs lançadas por aí. Na última semana com a adição das novas APIs do Google eles anunciaram que já tem mais de 500 APIs catalogadas!

Categories
REST SOAP Webservices

A guerra entre REST e WS-* acabou!

Acabo de ler um artigo no InfoQ entitulado The REST versus WS-* war is over!.

Só que pra ser bem sincero eu não concordo muito com este artigo. Para mim essa guerra nunca existiu.

Veja bem, é mais ou menos como tentar fazer uma guerra entre facas de pão e facas de peixe para tentar descobrir qual é a melhor. Oras, se você está comendo peixe, a melhor faca é a faca de peixe. Se você vai cortar um pão, a melhor faca é a de pão. Tá bom, essa analogia foi ridícula, mas é tão ridícula quanto uma comparação entre REST e WS-*/SOAP.

O que eu quero dizer é que, assim como funciona com as facas, o tipo de webservice que se usa varia em função do cenário de uso.

Nem todos os tipos de sistemas precisam da quantidade de recursos que WS-*/SOAP oferecem e isso torna-se um overhead desnecessário. Por exemplo, é muito mais fácil fazer mashups e sites com vários recursos Ajax utilizando webservices REST. Em compensação você pode precisar fazer orquestrações de webservices, operações transacionais, roteamento e outras coisas que REST não oferece mas que com WS-*/SOAP se faz com relativa facilidade.

Certamente há espaço para os dois no mercado, não vejo motivos para encararmos como uma guerra.

Categories
REST Webservices

POST vs. PUT: quem insere e quem altera?

Eu e o Phillip Calçado estamos trabalhando numa aplicação com uma interface de webservices REST.

Um dos pontos que estavamos discutindo hoje é sobre os métodos POST e PUT. Qual deles que representa um INSERT e qual representa um UPDATE? Se você procurar na internet vai perceber que vários sites dizem várias coisas diferentes. Foi quando resolvemos mergulhar no problema e entender qual é a forma correta de se fazer as coisas.

Superficialmente falando, webservices REST são CRUDs (acrônimo para Create, Read, Update e Delete). Por isso é muito natural tentar traçar um paralelo entre os métodos HTTP e as operações de banco de dados feitas por um CRUD

HTTP -> SQL

  • GET -> SELECT
  • DELETE -> DELETE
  • POST -> UPDATE
  • PUT -> INSERT

O nosso erro foi justamente esse de tentar traçar um paralelo com as ações de um CRUD. Se você pensar em SQL, realmente faz sentido que o POST seja um UPDATE e o PUT um INSERT. Porém quando se trata de webservices REST a coisa fica um pouco diferente porque HTTP é um pouco diferente de SQL.

O funcionamento do método PUT é “colocar” uma página numa determinada URL. Se a página já existir naquela URL ela é substituída. Se não houver página, ela é criada e passa a ser a que foi enviada no PUT. PUT é uma operação limitada que simplesmente coloca uma página numa localidade. Além disso, PUT é idempotente, ou seja, se você fizer 20 vezes um PUT numa URL o resultado tem que ser o mesmo que seria se você fizesse uma vez só: é o mesmo que acontece se você executar 20 UPDATES em um registro, é o mesmo que executar um update só.

Para exemplificar, considere que estamos criando um usuário num sistema qualquer. Neste sistema, a URL para obter (GET) um usuário de ID 1, por exemplo, seria:

http://gc.blog.br/usuarios/1

Então, se você desejasse criar um usuário utilizando PUT, a URL teria que ser:

http://gc.blog.br/usuarios/[id]

Porém quando você cria um objeto você normalmente não sabe qual é a chave primária/id deste objeto. Quem te dá esta informação é o sistema, não é você que escolhe. Neste caso, se você fizer um PUT numa URL de um usuário que já existe, você o AUTALIZARIA ao invés de criar um usuário novo.

Para criar um usuário o correto seria fazer um POST para a URL:

http://gc.blog.br/usuarios

Ao receber um POST esta URL cria um novo usuário e retorna o ID dele no response. O POST não é idempotente e se esta URL for chamada 20 vezes, 20 usuários serão criados e cada um deles terá um ID diferente: o mesmo que acontece quando você executa 20 INSERTS. Esta URL pode ser considerada como uma Factory de objetos Usuario.

Sendo assim a forma correta de se mapear os métodos HTTP para ações de um CRUD é:

  • GET -> SELECT
  • DELETE -> DELETE
  • POST -> INSERT
  • PUT -> UPDATE

Em alguns casos, porém, você sabe qual é a URL que será criada para o usuário. Por exemplo, os seus usuários poderiam ser representados da seguinte forma:

http://gc.blog.br/usuarios/guilherme

Ou seja, para criar um usuário com PUT você poderia chamar uma URL com o padrão:

http://gc.blog.br/usuarios/[login]

Porém esta operação é extremamente arriscada. Se já existir um usuário “guilherme” o sistema achará que você está tentando atualizar o Guilherme ao invés de criar um novo e o “guilherme” antigo irá se perder. Se for um POST o sistema pode retornar uma mensagem dizendo que não pôde criar o “guilherme” porque já existe um usuário com este login.

Categories
Notícias REST Webservices

IDLs para REST

Ultimamente têm se discutido muito se webservices REST precisam ou não de uma IDL (Interface Description Language) que seria mais ou menos o que o WSDL é para webservices SOAP.

Enquanto a discussão rola, alguns já começaram a desenvolver soluções, como é o caso de Thomas Steiner que lançou a versão 0.3 da sua ferramenta REST Describe & Compile tool.

Para ver que tipo de saída é gerada pela ferramenta, você pode testar a versão online do REST describe. Caso você não tenha uma URL para testar, use algum dos webservices da Last.fm (Audioscrobbler) como este que obtém os 50 artistas que eu mais ouço: http://ws.audioscrobbler.com/1.0/user/gchapiewski/topartists.xml.