Rodrigo Kumpera Weblog

Meus achados sobre tecnologia

Modelos de I/O e suas lições de escalabilidade

March 30th, 2006 · 1 Comment

Acho engraçado como algumas lições sobre escalabilidade são tão dificeis para as pessoas aprenderem. Um exemplo claro disso é como dados chegam de uma camada a outra de um sistema. Pode parecer meio bobo, mas vamos ver como uma lição simples oriunda da programação com sockets é perfeitamente aplicavel a arquitetura de um sistema.

Voltemos então a sockets 101, aos modelos de uso, que são, a grosso modo: bloqueante, não-bloqueante, multiplexado, assíncrono e/ou orientado a eventos.

No modelo bloqueante, a chamada ao SO somente completa quando os dados ficam disponíveis para o programar ler. É ruim porque a thread em questão fica em espera até terminar a recepção dos dados, mas é muito interessante já que simplifica em muito o modelo de programação. Este é o modelo de streams ao qual estamos mais que acostumados.

No modelo não-bloqueante, toda chamada ao SO retorna imediatamente dizendo se tinha algo disponivel ou não. Esse modelo é ruim porque é necessario ficar o tempo todo verificando se existem dados disponíveis. A vantagem é que permite ao cliente fazer outras coisas enquanto os dados não estão disponíveis.

No modelo multiplexado, na chamada ao SO se passa um conjunto de sockets e ela retorna somente depois que algum evento relativo a um dos sockets ocorrer. Ou seja, em uma requisição só ao sistema podemos verificar se existem dados disponíveis para vários lugares diferentes. A vantagem, como já deve ter estar clara, é a maior escalabilidade, já que uma thread só consegue cuida de forma sã de muitos sockets. A dificuldade deste modelo fica por conta da maior complexidade da solução e da integração com o resto do sistema, não é interessante que ocorram chamadas bloqueantes na thread que faz a multiplexação dos sockets.

Por fim temos o modelo assíncrono, nele a aplicação quando chama o SO informa uma função de callback para ser chamada quando o processamento terminar. Simples assim, você pede para ler do socket, volta a fazer suas coisas e o sistema te avisa quando a leitura completou. Seria facil se não fosse o modelo mais dificil de trabalhar, já que existem uma quantidade enorme de problemas relacionados a concorrência e reentrancia nesse modelo – o SO vai entregar a notificação a qualquer momento, que pode ser exatamente o meio da execução de uma função não reentrante.
A relevancia disso está no modelo de requisição e entrega das informações. Isso se traduz diretamente na forma como escrevemos nossas aplicações no dia-a-dia. Nos modelos bloqueante e assíncrono a informação é “empurrada” para o cliente pelo servidor, é o conhecido por push-based. No modelo não-bloqueante o cliente precisa repetidamente consultar a fonte de dados por novidades, é classificado por pull-based. Por fim, com socket multiplexados temos um caso curioso, pois a notificação de que dados estão disponíveis é push-based mas o recebimento pode ser feito de qualquer forma.

Dito isto, podemos classificar os modelos de comunicação da seguinte forma:

  • Bloqueante, syncronous push-based delivery, ou seja, o cliente avisa que deseja receber os dados e fica esperando até serem entregues.
  • Não-bloqueante, pull-based delivery, isto é, o cliente realiza consultas para ver se os dados estão disponíveis até que isso ocorra.
  • Multiplexado, synchronous push-based notification, em vez do cliente receber os dados que deseja, ele recebe apenas uma notificação que eles estão disponíveis.
  • Assíncrono, asynchronous push-based delivery, os dados são entregues ao cliente e este é notificado que eles já estão disponíveis.

Em resumo, podemos classificar cada modelo de acordo com a sincroneidade dele – se o cliente espera ou não por uma transição de estados; modelo de entrega – o cliente consulta a fonte (pull-based) ou recebe diretamente (push-based); e pelo tipo de evento que ocorre – notificação de disponibilidade ou entrega dos dados.

Estes modelos podem ser classificados quanto a escalabilidades na seguinte ordem: não-bloqueante, bloqueante, muliplexado e assíncrono quando se trata de I/O. O modelo não-bloqueante é mais interessante se o cliente for stateless. Estamos todos acostumados a programar sistemas sem estado por serem muito mais faceis de escalar, o problema é que sem uma tier statefull não é possivel ir além dos limites do backend que fornece os dados. Só que você já se perguntou se o seu sistema usa o melhor modelo de comunicação entre os tier dele?

Tags: Programming · Scalability

1 response so far ↓

  • 1 Luca // Jun 2, 2006 at 5:58 pm

    Por incrível que pareça, só hoje conheci seu blog. Gostei muito de tudo. Este tópico está muito bem escrito.

    Agora, se houver um próximo sobre o mesmo assunto, seria legal citar a existência do Seda, Mule-esb, Mina, Grizzly, etc. dizendo ao povo onde cada um se enquadra. E falar também como o modelo de mensageria usado pelo JMS pode se comparar com estas soluções.

Leave a Comment