InfoQ

InfoQ

Notícias

Meus Favoritos

Faça oLogin ou Cadastre-se para ativar o recurso de favoritos por tempo ilimitado.

O conteúdo foi adicionado aos favoritos!

Houve um erro ao adicionar aos favoritos! Por favor, tente novamente.

LMAX: 6 milhões de transações por segundo com uma única thread

Postado por Julio Faerman em 02 Ago 2011

Seções
Arquitetura Corporativa,
Operações e Infraestrutura,
Desenvolvimento
Tópicos
Java ,
Operações ,
Linguagens ,
Arquitetura ,
Programação ,
Desempenho e Escalabilidade ,
Arquitetura Corporativa

Martin Fowler, autor de diversos livros essenciais sobre arquitetura e desenvolvimento de sistemas corporativos, publicou recentemente em seu blog uma descrição detalhada da arquitetura da plataforma financeira LMAX. Também apresentou como foi desenvolvida a plataforma de modo a atingir uma impressionante taxa de 6 milhões de transações por segundo, usando apenas uma thread, usando hardware comum:

A medição de 6 milhões de transações por segundo foi realizada em um servidor Dell Nehalem quad core dual socket de 3Ghz com 32 GB RAM.

A plataforma financeira LMAX foi apresentada em 2010, nos QCon de Londres e de São Francisco, este último com gravação do InfoQ.  As apresentações tiveram grande repercussão em sites e blogs sobre arquitetura de sistemas. 

As plataformas financeiras em geral têm requisitos complexos, mas esta tinha um novo complicador. Ao invés de ser vendida para poucos clientes corporativos, seria oferecida a clientes no varejo, com uma expectativa de altíssimo volume de transações por segundo.

Tendo lidado com diversos problemas de desempenho no desenvolvimento de um sistema de apostas esportivas, a equipe do LMAX decidiu explorar arquiteturas alternativas às soluções tradicionais envolvendo coordenação de sessões com bancos de dados relacionais. 

Paralelismo e filas

O primeiro protótipo da arquitetura, com foco em paralelismo, foi construído usando o modelo de atores. Neste modelo ao invés de se controlar a concorrência com as primitivas tradicionais (Mutex, Semaforo etc.), são utilizados objetos de alto nível como atores e mensagens. 

Apesar de ter sua origem na década de 70, o modelo de atores tem ganhado popularidade apenas recentemente, devido a dificuldades apresentadas no controle de concorrência entre múltiplos núcleos de processamento, como os encontrados nas CPUs modernas. No entanto, o modelo já é cidadão de primeira classe em linguagens como Scala e Erlang.

A equipe do LMAX foi até a raiz do problema na avaliação do desempenho do protótipo e observou que o gerenciamento de filas pelos atores é ineficiente do ponto de vista de uso do cache interno do processador, cujo acesso é muito mais rápido que o acesso à RAM. Fowler comenta:

Para colocar alguma informação em uma fila, é preciso (obviamente) escrever naquela fila. De modo similar, para retirar dados da fila, deve-se escrever na fila para realizar a remoção. Há portanto uma contenção de escrita: mais que um cliente pode precisar escrever na mesma estrutura de dados. Para lidar com esse tipo de contenção em filas, normalmente utilizam-se travas (locks). Mas se uma trava é usada, isso pode causar uma troca de contexto com o kernel e quando isso ocorre, o processador provavelmente perderá os dados em seus caches. 

A conclusão foi que, para otimização do cache, é necessário que apenas um núcleo do processador escreva em uma dada região de memória. Devido a esta restrição, surgiram duas decisões importantes. A primeira foi criar uma nova estrutura para controle de concorrência sem travas, batizada de "Disruptor". A segunda foi investigar o quão rápido pode ser o processamento da lógica de negócio em uma única thread, se não for usado o controle de concorrência.

Disruptor

A estrutura de controle Disruptor funciona como uma fila circular para múltiplos clientes. Nessa fila, tudo o que é adicionado pelos produtores é disponibilizado paralelamente aos consumidores. São utilizados apenas contadores simples, ao invés de travas, para garantir que o limites de leitura não sejam ultrapassados. 

No caso do LMAX, todo evento produzido é processado por três consumidores antes de chegar à lógica de negócio (veja a figura): o journaler, o replicator e o unmarshaller:

  • O papel do journaler é persistir todos os eventos. Para isso, não é usado um banco de dados, e sim apenas arquivos sequenciais, cujo acesso no disco é muito eficiente. 
  • O replicator retransmite os eventos para nós redundantes, que são usados para alta disponibilidade. 
  • O unmarshaller desserializa os eventos para objetos Java, que serão consumidos pela lógica de negócio. 

O artigo de Fowler descreve em mais detalhes os componentes e suas possíveis variações. A empresa LMAX (que deu nome à arquitetura) disponibilizou o Disruptor como um framework sob licença Apache no Google Code, mostrando inovação considerável em um mercado que é geralmente "segredista".

Dave Farley, co-autor do livro Continuous Delivery e membro da empresa LMAX, apresenta em seu blog mais detalhes sobre a lógica de negócio, que é implementada em interfaces e objetos comuns, possibilitando o uso de modelos orientados a objetos ricos e flexíveis. Farley conclui:

Orgulho-me da nossa infraestrutura tecnológica, mas para mim o ganho real não é o grau de sofisticação da arquitetura, mas sim o quanto ela libertou a equipe para se concentrar nos problemas de negócio que somos pagos pra resolver.

Thread única

Com a arquitetura descrita, o processamento da lógica de negócio pode ser executado em apenas uma thread, pois os Disruptors gerenciam I/O e concorrência. Os Disruptors e o cache "aquecido" – que é acessado o máximo possível antes de se se buscar na memória principal – foram fatores essenciais para se atingir o altíssimo desempenho medido.

Fowler ressalta ainda o quanto os testes de desempenho foram importantes para se chegar a tais resultados, mas alerta:

Criar código de testes de alto desempenho pode ser tão difícil quanto escrever código para produção. É comum obter resultados falsos, devido aos testes não serem tão rápidos quanto o componente sob medição.

Aplicações

Na sua conclusão, Martin Fowler apresenta considerações importantes sobre como podemos aproveitar a arquitetura LMAX em nossos sistemas. Apesar de o cenário da LMAX ser um tanto particular e extremo, as decisões e componentes utilizados são comuns em arquiteturas modernas. 

Manter os dados em memória RAM e usar o disco apenas para restauração e backup, por exemplo, é uma estratégia comum em produtos de grid e noSQL, como Infinispan, Coherence, Prevayler, entre outros. A maneira como os eventos são utilizados é semelhante ao sistema CQRS, apesar de este não ser usado explicitamente pelo LMAX. (Para uma introdução ao CQRS, veja o artigo de Fowler sobre o assunto e essa palestra de Pedro Teixeira, realizada no ultimo QCon São Paulo.) 

Mesmo que a sua arquitetura seja menos estrita e use muito menos recursos, os dilemas e decisões enfrentados pela LMAX são certamente instrutivos e reaproveitáveis em diversas situações.

Leandro por leandro moreira Enviado
Re: Leandro por Julio Faerman Enviado
Thread única? por Fernando Lozano Enviado
Re: Thread única? por Julio Faerman Enviado
Re: Thread única? por Fernando Lozano Enviado
Re: Thread única? por Fernando Lozano Enviado
Re: Thread única? por Julio Faerman Enviado
Re: Thread única? por José Filipe Neis Enviado
Re: Thread única? por Julio Faerman Enviado
Excelente.... por Natanael Fonseca Enviado
Re: Excelente.... por Julio Faerman Enviado
Muito bom padô! por Jairo Rodrigues Enviado
  1. Voltar ao topo

    Leandro

    por leandro moreira

    Muito bom artigo, talvez substituir : cache "aquecido" por cache recente.

  2. Voltar ao topo

    Thread única?

    por Fernando Lozano

    Perdoem a crítica, mas o título ficou meio enganador, pois a arquitetura LMAX descrita pelo artigo do Martin Fowler NÃO USA uma ÚNICA thread: os disruptors de entrada e de saída tem vários threads independentes do thread que roda o business processor. O que ela demonstra é uma maneira de se trabalhar com múltiplos threads, algo inevitável em qualquer sistema de servidor, com o mínimo de lock e máximo aproveitamento dos caches e consquentemente das CPUs.

    Além disso, a arquiteura exige que existam outras instâncias do conjunto disruptors + business processors rodando em paralelo, para que não haja perda de serviço em caso de problemas com uma das instâncias, o que obrigaria a uma potencialmente demorada re-execução de toda a fila de eventos.

    A equipe do LMAX também gera snapshots diários de cada instância e reinicia cada uma para manter um nível de serviço alto. TODO o estado do sistema está contido no último smapshot + fila de eventos posteriores, não existe nenhum outro armazenamento persistente nesta arquitetura, em especial não existe um BD.

    O LMAX é uma arquitetura muito interessante e de sucesso comprovado, mas não pensem que é trivial trabalhar hoje em dia com qualquer arquitetura diferente do modelo transacional baseado em bancos de dados relacionais. ;-)

  3. Voltar ao topo

    Re: Thread única?

    por Julio Faerman

    Não foi afirmado que o sistema usa uma única thread, mas que uma única thread processa 6M transações (logica de negócio) por segundo.

  4. Voltar ao topo

    Re: Leandro

    por Julio Faerman

    Obrigado leandro, não conhecia o termo. Vício do inglês,

  5. Voltar ao topo

    Re: Thread única?

    por Fernando Lozano

    Seria 6M transações em uma única thread se pudéssemos considerar apenas a execução do business processor. Mas as transações do LMAX envolvem também os disruptors de entrada e de saída. Não podemos considerar uma transação completa como envolvendo apenas o business processor porque sem os disruptors (e as filas de eventos associadas) não tem como chegar trabalho nos business processors nem é possível assegurar a integridade das transações.

    Eu concordo que o artigo original não deixa claro se são 6M transações considerando apenas o business processor ou se considerando a arquitetura como um todo, mas eu não vejo como poderia ser diferente (de considerar tudo).

  6. Voltar ao topo

    Re: Thread única?

    por Fernando Lozano

    Pense bem, 6 MILHÕES de tps envolvendo só de operações sobre memória, sem nenhuma operação de E/S de disco nem de rede, não é nada de mais em uma CPU x86 moderna, que é capaz de executar dezenas de BILHÕES de instruções por segundo. Este valor só se torna significativo se envolver alguma E/S e portanto os disruptores e seus respectivos threads.

  7. Voltar ao topo

    Excelente....

    por Natanael Fonseca

    Parabéns pelo artigo !

  8. Voltar ao topo

    Re: Thread única?

    por José Filipe Neis

    Considerando que na verdade são passos separados, acho que sim, poderia ser considerada como uma transação lógica, apesar de não representar a transação de negócio.

    De qualquer maneira, não acho que o número de tps é o ponto mais importante do artigo. O throughput dos caras vai ser algo assustador de qualquer maneira. A grande sacada, a meu ver, está numa maneira melhor de trabalhar com os locks. Como o Fernando colocou no comentário anterior, não é simples, mas cenários de alto volume (como o do LMAX) tem sérias dificuldades pra rodar com o tradicional modelo de BD relacional (na verdade, o volume não precisa nem ser tão alto assim :)).

  9. Voltar ao topo

    Re: Thread única?

    por Julio Faerman

    O problema é que como o I/O é assíncrono, fica muito difil medir isso. As transações "do negócio" são um lugar bom pra colocar a medição, mas sim, não leva em conta o sistema como um todo.

  10. Voltar ao topo

    Re: Excelente....

    por Julio Faerman

    Obrigado!

  11. Voltar ao topo

    Re: Thread única?

    por Julio Faerman

    Com certeza o desempenho não é o mais importante, mas é o que chama mais atenção :)

  12. Voltar ao topo

    Muito bom padô!

    por Jairo Rodrigues

    Obrigado por essa leitura de qualidade. Animará os acadêmicos à darem mais atenção as matérias fundamentais da computação (estrutura de dados, arq de computadores, SO, sistemas distribuidos, etc).