Sunday, 11 March 2018

Sistema de negociação zeromq


zeromq.


Copyright (c) 2010 iMatix Corporation e colaboradores.


É concedida permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e dos arquivos de documentação associados (o "Software"), para negociar no Software sem restrições, incluindo, sem limitação, os direitos de uso, cópia, modificação, mesclar, publicar, distribuir, sublicenciar e / ou vender cópias do Software, e permitir que pessoas a quem o Software seja fornecido façam isso, sujeitas às seguintes condições:


O aviso de copyright acima e este aviso de permissão devem ser incluídos em todas as cópias ou partes substanciais do Software.


O SOFTWARE É FORNECIDO & quot; COMO ESTÁ & quot ;, SEM GARANTIA DE QUALQUER TIPO, EXPRESSA OU IMPLÍCITA, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE COMERCIALIZAÇÃO, ADEQUAÇÃO A UM DETERMINADO FIM E NÃO VIOLAÇÃO. EM NENHUMA CIRCUNSTÂNCIA, OS AUTORES OU PROPRIETÁRIOS DE DIREITOS DE AUTOR PODERÃO SER RESPONSABILIZADOS POR QUAISQUER REIVINDICAÇÕES, DANOS OU OUTRAS RESPONSABILIDADES, QUER EM ACÇÃO DE CONTRATO, DELITO OU DE OUTRA FORMA, DECORRENTES DE, OU EM CONEXÃO COM O SOFTWARE OU O USO OU OUTRAS NEGOCIAÇÕES NO PROGRAMAS.


AVISO: este texto está obsoleto e refere-se a uma versão antiga do ØMQ. Permanece aqui por interesse histórico. NÃO USE ISTO PARA APRENDER ØMQ.


Introdução.


Como o MMQ tem como principal objetivo impulsionar os negócios de negociação de ações, criamos um aplicativo de amostra que simula o funcionamento interno de uma bolsa de valores. O foco principal deste exemplo é mostrar como o MQ opera no cenário do mundo real.


O diagrama abaixo mostra a arquitetura do aplicativo:


O componente de gateway deve receber pedidos de traders pela rede (usando o protocolo FIX ou um protocolo proprietário específico) e enviar respostas de volta aos traders. No entanto, como o aplicativo de exemplo destina-se a mostrar quais são os possíveis débitos / latências de tal sistema, o gateway gera pedidos aleatórios em vez de recebê-los dos operadores. Ao iniciar o gateway, você pode especificar o número de pedidos por segundo a serem gerados.


Unidade de correspondência contém o núcleo da lógica de negócios da bolsa de valores. Ele corresponde os pedidos de um para o outro e produz negociações e cotações. Nossa implementação é minimalista, baseada no algoritmo de correspondência tempo / preço (a implementação do algoritmo pro rata é deixada como um exercício para o leitor). Ainda assim, o algoritmo tem complexidade de O (1) e é altamente otimizado. Nós vimos processar cerca de 18 milhões de pedidos por segundo.


O componente de estatísticas recebe informações de desempenho geradas pelo gateway e pelo mecanismo de correspondência e as exibe em formato legível. Para tornar a leitura de estatísticas ainda mais conveniente, uma ferramenta gráfica simples é incluída no exemplo.


Atuação.


A captura de tela a seguir mostra o desempenho do exemplo em duas caixas high-end de 8 núcleos (3GHz), cada uma com 2 NICs dedicadas de 1 GbE. Lembre-se de que, se você executá-lo no hardware não especificado e / ou não ajustado, ele ainda pode ser muito rápido. No entanto, você experimentará menores taxas de transferência de mensagens e mais picos de latência. Se você definir a taxa de mensagens muito alta, poderá até mesmo experimentar falha de gatway, já que o componente ticker usado para enviar pedidos na taxa estável não conseguirá acompanhar o ritmo.


A linha amarela mostra a latência de ida e volta, ou seja, quanto tempo demorou para passar a ordem do gateway para o mecanismo de correspondência, processá-lo e enviar a confirmação do pedido de volta ao gateway. Em nossa latência de teste flutuou em torno de 200 microssegundos de média.


A linha de transferência mais baixa (900.000 mensagens / segundo) é a taxa de pedidos que estão sendo transmitidos do gateway para o mecanismo correspondente. A linha de produção superior (2.300.000 mensagens / segundo) é a taxa de confirmações de pedidos, negociações e cotações de ações passadas do mecanismo correspondente para o gateway. No total, vimos aproximadamente 3.200.000 mensagens por segundo passando pela rede.


Construindo.


Para construir o exemplo use a opção --with-exchange com configure:


Para poder rodar a ferramenta gráfica, você precisa ter o Perl-Tk instalado (empacotado como perl-tk no Debian), assim como o Tk :: Graph do CPAN.


Correndo isto.


Por exemplo, temos a seguinte topologia de rede para executar o exemplo. Caixas representam máquinas individuais, setas representam cabos físicos entre interfaces de rede individuais (marcadas por seus respectivos endereços IP):


Existem 3 caixas (test01, test02 e test03) conectadas à rede comutada com os respectivos endereços IP 192.168.0.1, 192.168.0.2 e 192.168.0.3. Além disso, existem duas conexões diretas entre o test02 e o test03. Uma conexão conecta a interface de rede 10.0.0.1 no test02 com a interface de rede 10.0.0.2 no test03. Outro conecta a interface de rede 10.1.0.1 no test02 com a interface de rede 10.1.0.2 no test03.


Vamos executar o zmq_server e o componente estatístico no test01, o mecanismo de correspondência no test02 e o gateway no test03. Usaremos uma das conexões diretas entre test02 e test03 para passar ordens de gatway para o mecanismo de correspondência e a outra para passar confirmações, comércios e cotações do mecanismo de correspondência para o gateway.


Primeiro, inicie o zmq_server no test01:


Depois disso, inicie o componente estatístico no test01. Os parâmetros são a caixa onde o zmq_server está sendo executado e a interface de rede para receber informações estatísticas sobre:


Alternativamente, você pode enviar os dados estatísticos para a ferramenta gráfica:


Agora inicie o mecanismo de correspondência. Forneça o nome do host do zmq_server, a interface para receber mensagens e a interface para enviar mensagens como parâmetros:


Finalmente, corra o gatway. Local de fornecimento de zmq_server e o número de pedidos a enviar por segundo como parâmetros:


Conclusão.


O exemplo do Exchange permite que você teste o desempenho do MMQ no cenário do mundo real. No entanto, obter latência estável em altas taxas de transferência é uma tarefa complicada, dependendo do ajuste geral do hardware, do sistema operacional, do ambiente de execução, etc. Se você levar a sério os testes de desempenho, entre em contato para ajudá-lo na tarefa.


Se você achou essa página útil, avalie-a para que outros a encontrem.


Quem está vigiando esta página?


O design e o conteúdo do site são de direitos autorais (c) 2014 iMatix Corporation. Entre em contato conosco para suporte profissional. Conteúdo do site licenciado sob cc-by-sa 3.0 ØMQ é copyright (c) Copyright (c) 2007-2014 iMatix Corporation e colaboradores. O MQ é um software livre licenciado sob a LGPL. ØMQ e ZEROMQ são marcas comerciais da iMatix Corporation. Termos de Uso & # 8212; Política de Privacidade.


ZeroMQ.


& Oslash; O MQ é um sistema de mensagens, ou "middleware orientado a mensagens", se você quiser. É usado em ambientes tão diversos quanto serviços financeiros, desenvolvimento de jogos, sistemas embarcados, pesquisa acadêmica e aeroespacial.


Os sistemas de mensagens funcionam basicamente como mensagens instantâneas para aplicativos. Um aplicativo decide comunicar um evento para outro aplicativo (ou vários aplicativos), monta os dados a serem enviados, acessa o botão "enviar" e lá vamos nós - o sistema de mensagens cuida do resto.


Ao contrário das mensagens instantâneas, porém, os sistemas de mensagens não têm GUI e não assumem nenhum ser humano nos terminais capazes de intervenção inteligente quando algo dá errado. Sistemas de mensagens, portanto, precisam ser tolerantes a falhas e muito mais rápidos do que as mensagens instantâneas comuns.


O Oslash foi originalmente concebido como um sistema de mensagens ultrarrápidas para negociação de ações e, portanto, o foco era a otimização extrema. O primeiro ano do projeto foi gasto na elaboração de metodologia de benchmarking e na tentativa de definir uma arquitetura o mais eficiente possível.


Mais tarde, aproximadamente no segundo ano de desenvolvimento, o foco mudou para fornecer um sistema genérico para construir aplicativos distribuídos e suportar padrões arbitrários de mensagens, vários mecanismos de transporte, ligações arbitrárias de linguagem, etc.


Durante o terceiro ano, o foco foi principalmente melhorar a usabilidade e achatar a curva de aprendizado. Adotamos a API BSD Sockets, tentamos limpar a semântica dos padrões de mensagens individuais e assim por diante.


Esperançosamente, este capítulo dará uma visão de como os três objetivos acima se traduziram na arquitetura interna do & Oslash; MQ, e fornecem algumas dicas para aqueles que estão lutando com os mesmos problemas.


Desde seu terceiro ano, Oslash, MQ superou sua base de código; existe uma iniciativa para padronizar os protocolos de ligação que ele usa e uma implementação experimental de um sistema de mensagens do tipo Oslash; MQ dentro do kernel do Linux, etc. Esses tópicos não são abordados neste livro. No entanto, você pode verificar os recursos on-line para obter mais detalhes: 250bpm / concepts, groups. google/group/sp-discuss-group e 250bpm / hits.


24,1. Aplicativo vs. Biblioteca.


& Oslash; o MQ é uma biblioteca, não um servidor de mensagens. Levamos vários anos trabalhando no protocolo AMQP, uma tentativa do setor financeiro de padronizar o protocolo para mensagens corporativas, escrevendo uma implementação de referência para ele e participando de vários projetos de larga escala fortemente baseados em tecnologia de mensagens para perceber que há algo errado com o modelo clássico de cliente / servidor do servidor de mensagens inteligente (broker) e clientes de mensagens idiotas.


Nossa principal preocupação na época era com o desempenho: se há um servidor no meio, cada mensagem tem que passar pela rede duas vezes (do remetente para o corretor e do corretor para o receptor) induzindo uma penalidade em termos de latência e taxa de transferência. Além disso, se todas as mensagens forem passadas pelo intermediário, em algum momento ele se tornará o gargalo.


Uma preocupação secundária estava relacionada a implantações em grande escala: quando a implantação ultrapassa os limites organizacionais, o conceito de uma autoridade central que gerencia todo o fluxo de mensagens não se aplica mais. Nenhuma empresa está disposta a ceder o controle a um servidor em empresa diferente; existem segredos comerciais e existe responsabilidade legal. O resultado na prática é que há um servidor de mensagens por empresa, com pontes escritas a mão para conectá-lo a sistemas de mensagens em outras empresas. Todo o ecossistema é, portanto, muito fragmentado, e manter um grande número de pontes para todas as empresas envolvidas não melhora a situação. Para resolver esse problema, precisamos de uma arquitetura totalmente distribuída, uma arquitetura na qual cada componente possa ser possivelmente governado por uma entidade comercial diferente. Dado que a unidade de gerenciamento na arquitetura baseada em servidor é o servidor, podemos resolver o problema instalando um servidor separado para cada componente. Nesse caso, podemos otimizar ainda mais o design fazendo com que o servidor e o componente compartilhem os mesmos processos. O que acabamos com é uma biblioteca de mensagens.


& Oslash; o MQ foi iniciado quando tivemos uma ideia sobre como fazer o envio de mensagens sem um servidor central. Isso exigia inverter todo o conceito de mensagens e substituir o modelo de um armazenamento autônomo centralizado de mensagens no centro da rede por uma arquitetura "smart endpoint, dumb network" baseada no princípio end-to-end. A consequência técnica dessa decisão foi que o & Oslash; MQ, desde o início, era uma biblioteca, não uma aplicação.


Enquanto isso, conseguimos comprovar que essa arquitetura é mais eficiente (menor latência, maior taxa de transferência) e mais flexível (é fácil criar topologias complexas arbitrárias em vez de ficarem vinculadas ao modelo clássico de hub e spoke).


Uma das conseqüências não intencionais, no entanto, foi que a opção pelo modelo de biblioteca melhorou a usabilidade do produto. Repetidamente, os usuários expressam sua felicidade pelo fato de não precisarem instalar e gerenciar um servidor de mensagens autônomo. Acontece que não ter um servidor é uma opção preferida, pois reduz o custo operacional (não é necessário ter um administrador de servidor de mensagens) e melhora o tempo de colocação no mercado (não é necessário negociar a necessidade de executar o servidor com o cliente, gestão ou a equipe de operações).


A lição aprendida é que, ao iniciar um novo projeto, você deve optar pelo design da biblioteca, se possível. É muito fácil criar uma aplicação a partir de uma biblioteca, invocando-a a partir de um programa trivial; no entanto, é quase impossível criar uma biblioteca a partir de um executável existente. Uma biblioteca oferece muito mais flexibilidade aos usuários, ao mesmo tempo em que poupa esforços administrativos não triviais.


24,2. Estado Global.


Variáveis ​​globais não jogam bem com bibliotecas. Uma biblioteca pode ser carregada várias vezes no processo, mas mesmo assim existe apenas um único conjunto de variáveis ​​globais. A Figura 24.1 mostra uma biblioteca do & Oslash; MQ sendo usada de duas bibliotecas diferentes e independentes. O aplicativo usa essas duas bibliotecas.


Figura 24.1: & Oslash; MQ sendo usado por diferentes bibliotecas.


Quando tal situação ocorre, ambas as instâncias do & Oslash; MQ acessam as mesmas variáveis, resultando em condições de corrida, falhas estranhas e comportamento indefinido.


Para evitar esse problema, a biblioteca do & Oslash; MQ não possui variáveis ​​globais. Em vez disso, um usuário da biblioteca é responsável por criar o estado global explicitamente. O objeto que contém o estado global é chamado de contexto. Enquanto do contexto da perspectiva do usuário parece mais ou menos como um pool de threads de trabalho, da perspectiva do & Oslash; MQ é apenas um objeto para armazenar qualquer estado global que precisaremos. Na figura acima, a libA teria seu próprio contexto e a libB também teria seu próprio contexto. Não haveria maneira de um deles quebrar ou subverter o outro.


A lição aqui é bem óbvia: não use o estado global nas bibliotecas. Se você fizer isso, a biblioteca provavelmente será interrompida quando for instanciada duas vezes no mesmo processo.


24,3. Atuação.


Quando o & Oslash; MQ foi iniciado, seu principal objetivo era otimizar o desempenho. O desempenho dos sistemas de mensagens é expresso usando duas métricas: throughput & mdash; quantas mensagens podem ser passadas durante um determinado período de tempo; e latência & mdash; quanto tempo leva para uma mensagem passar de um ponto final para o outro.


Qual métrica devemos nos concentrar? Qual a relação entre os dois? Não é óbvio? Execute o teste, divida o tempo total do teste pelo número de mensagens transmitidas e o que você recebe é a latência. Divida o número de mensagens por hora e o que você recebe é a taxa de transferência. Em outras palavras, a latência é o valor inverso da taxa de transferência. Trivial, certo?


Em vez de começar a codificar imediatamente, passamos algumas semanas investigando as métricas de desempenho em detalhes e descobrimos que a relação entre taxa de transferência e latência é muito mais sutil do que isso e, muitas vezes, as métricas são bastante contrárias à intuição.


Imagine Enviar mensagens para B. (Veja a Figura 24.2.) O tempo total do teste é de 6 segundos. Existem 5 mensagens passadas. Portanto, a taxa de transferência é de 0,83 msgs / seg (5/6) e a latência é de 1,2 seg (6/5), certo?


Figura 24.2: Enviando mensagens de A para B.


Dê uma olhada no diagrama novamente. Leva um tempo diferente para cada mensagem chegar de A a B: 2 seg, 2,5 seg, 3 seg, 3,5 seg, 4 seg. A média é de 3 segundos, o que é bem distante do nosso cálculo original de 1,2 segundo. Este exemplo mostra os equívocos que as pessoas estão intuitivamente inclinadas a fazer sobre as métricas de desempenho.


Agora dê uma olhada no throughput. O tempo total do teste é de 6 segundos. No entanto, em A leva apenas 2 segundos para enviar todas as mensagens. Da perspectiva de A, a taxa de transferência é de 2,5 msgs / seg (5/2). Em B leva 4 segundos para receber todas as mensagens. Então, da perspectiva de B, a taxa de transferência é de 1,25 msgs / seg (5/4). Nenhum desses números corresponde ao nosso cálculo original de 1,2 msgs / seg.


Para encurtar a história, a latência e a taxa de transferência são duas métricas diferentes; isso é óbvio. O importante é entender a diferença entre os dois e seu relacionamento mútuo. A latência pode ser medida apenas entre dois pontos diferentes no sistema; Não existe latência no ponto A. Cada mensagem tem sua própria latência. Você pode calcular a média das latências de várias mensagens; no entanto, não existe latência de um fluxo de mensagens.


A produtividade, por outro lado, pode ser medida apenas em um único ponto do sistema. Há um throughput no remetente, há um throughput no receptor, há um throughput em qualquer ponto intermediário entre os dois, mas não existe um throughput geral de todo o sistema. E a taxa faz sentido apenas para um conjunto de mensagens; Não há tal coisa como taxa de transferência de uma única mensagem.


Quanto à relação entre taxa de transferência e latência, verifica-se que realmente existe um relacionamento; no entanto, a fórmula envolve integrais e não vamos discutir isso aqui. Para mais informações, leia a literatura sobre a teoria das filas.


Há muitas outras armadilhas no benchmarking dos sistemas de mensagens nos quais não iremos nos aprofundar. O estresse deve ser colocado na lição aprendida: certifique-se de entender o problema que está resolvendo. Até mesmo um problema tão simples quanto "torná-lo rápido" pode exigir muito trabalho para entender corretamente. Além do mais, se você não entender o problema, é provável que você crie suposições implícitas e mitos populares em seu código, tornando a solução defeituosa ou, pelo menos, muito mais complexa ou muito menos útil do que poderia ser.


24,4. Caminho crítico.


Descobrimos durante o processo de otimização que três fatores têm um impacto crucial no desempenho:


Número de alocações de memória Número de chamadas do sistema Modelo de simultaneidade.


No entanto, nem toda alocação de memória ou todas as chamadas de sistema têm o mesmo efeito no desempenho. O desempenho em que estamos interessados ​​em sistemas de mensagens é o número de mensagens que podemos transferir entre dois pontos de extremidade durante um determinado período de tempo. Como alternativa, podemos estar interessados ​​em quanto tempo leva para uma mensagem passar de um ponto de extremidade para outro.


No entanto, dado que o & Oslash; MQ é projetado para cenários com conexões de longa duração, o tempo necessário para estabelecer uma conexão ou o tempo necessário para lidar com um erro de conexão é basicamente irrelevante. Esses eventos acontecem muito raramente e, portanto, seu impacto no desempenho geral é insignificante.


A parte de uma base de código que é usada com muita frequência, repetidamente, é chamada de caminho crítico; otimização deve se concentrar no caminho crítico.


Vamos dar uma olhada em um exemplo: & Oslash; O MQ não é extremamente otimizado em relação às alocações de memória. Por exemplo, ao manipular cadeias de caracteres, ele geralmente aloca uma nova cadeia para cada fase intermediária da transformação. No entanto, se olharmos estritamente para o caminho crítico - a passagem da mensagem real - descobriremos que ela quase não usa alocações de memória. Se as mensagens são pequenas, é apenas uma alocação de memória por 256 mensagens (essas mensagens são mantidas em um único grande pedaço de memória alocada). Se, além disso, o fluxo de mensagens estiver estável, sem grandes picos de tráfego, o número de alocações de memória no caminho crítico cai para zero (os blocos de memória alocados não são retornados ao sistema, mas são reutilizados repetidas vezes). .


Lição aprendida: otimize onde faz diferença. A otimização de partes do código que não estão no caminho crítico é um esforço desperdiçado.


24,5. Alocando memória.


Supondo que toda a infraestrutura foi inicializada e uma conexão entre dois pontos de extremidade foi estabelecida, há apenas uma coisa a ser alocada ao enviar uma mensagem: a própria mensagem. Assim, para otimizar o caminho crítico, precisamos analisar como as mensagens são alocadas e passadas para cima e para baixo na pilha.


É de conhecimento geral no campo de redes de alto desempenho que o melhor desempenho é obtido pelo balanceamento cuidadoso do custo da alocação de mensagens e do custo da cópia de mensagens (por exemplo, hal. inria. fr/docs/00/29/28/31/ PDF / Open-MX-IOAT. pdf: veja o tratamento diferente de mensagens "pequenas", "médias" e "grandes"). Para pequenas mensagens, a cópia é muito mais barata do que alocar memória. Faz sentido não alocar novos blocos de memória e, em vez disso, copiar a mensagem para memória pré-alocada sempre que necessário. Para mensagens grandes, por outro lado, a cópia é muito mais cara que a alocação de memória. Faz sentido alocar a mensagem uma vez e passar um ponteiro para o bloco alocado, em vez de copiar os dados. Essa abordagem é chamada de "cópia zero".


& Oslash; o MQ manipula os dois casos de maneira transparente. Uma mensagem do & Oslash; MQ é representada por um identificador opaco. O conteúdo de mensagens muito pequenas é codificado diretamente no identificador. Então, fazer uma cópia da alça copia os dados da mensagem. Quando a mensagem é maior, ela é alocada em um buffer separado e o identificador contém apenas um ponteiro para o buffer. Fazer uma cópia do identificador não resulta na cópia dos dados da mensagem, o que faz sentido quando a mensagem é longa em megabytes (Figura 24.3). Deve-se notar que, no último caso, o buffer é contado por referência, de modo que ele pode ser referenciado por várias alças, sem a necessidade de copiar os dados.


Figura 24.3: Cópia de mensagens (ou não)


Lição aprendida: Ao pensar em desempenho, não pense que existe uma única solução melhor. Pode acontecer que existam várias subclasses do problema (por exemplo, mensagens pequenas vs. mensagens grandes), cada uma com seu próprio algoritmo ótimo.


24,6. Agrupamento


Já foi mencionado que o grande número de chamadas de sistema em um sistema de mensagens pode resultar em um gargalo de desempenho. Na verdade, o problema é muito mais genérico do que isso. Há uma penalidade de desempenho não trivial associada à passagem da pilha de chamadas e, portanto, ao criar aplicativos de alto desempenho, é aconselhável evitar o máximo possível de movimentação de pilha.


Considere a Figura 24.4. Para enviar quatro mensagens, você tem que percorrer toda a pilha de rede quatro vezes (ou seja, & Oslash; MQ, glibc, limite de espaço do usuário / kernel, implementação TCP, implementação IP, camada Ethernet, a própria NIC e fazer backup da pilha novamente).


Figura 24.4: Enviando quatro mensagens.


No entanto, se você decidir juntar essas mensagens em um único lote, haverá apenas um percurso da pilha (Figura 24.5). O impacto na taxa de transferência de mensagens pode ser esmagador: até duas ordens de magnitude, especialmente se as mensagens forem pequenas e centenas delas puderem ser agrupadas em um único lote.


Figura 24.5: Mensagens em lote.


Por outro lado, os lotes podem ter impacto negativo na latência. Tomemos, por exemplo, o algoritmo bem conhecido do Nagle, conforme implementado no TCP. Ele atrasa as mensagens de saída por um determinado período de tempo e mescla todos os dados acumulados em um único pacote. Obviamente, a latência de ponta a ponta da primeira mensagem no pacote é muito pior que a latência da última. Assim, é comum que os aplicativos precisem consistentemente de baixa latência para desativar o algoritmo de Nagle. É até comum desligar os lotes em todos os níveis da pilha (por exemplo, o recurso de coalescência de interrupção da NIC).


Mas, novamente, nenhum lote significa deslocamento extensivo da pilha e resulta em baixa taxa de transferência de mensagens. Parece que estamos presos em um dilema de throughput versus latência.


& Oslash; o MQ tenta fornecer consistentemente baixas latências combinadas com alta taxa de transferência usando a seguinte estratégia: quando o fluxo de mensagens é esparso e não excede a largura de banda da pilha de rede, o Oslash; MQ desativa todos os lotes para melhorar a latência. O trade-off aqui é um pouco maior uso da CPU - ainda temos que percorrer a pilha com freqüência. No entanto, isso não é considerado um problema na maioria dos casos.


Quando a taxa de mensagens excede a largura de banda da pilha de rede, as mensagens precisam ser enfileiradas & mdash; armazenadas na memória até que a pilha esteja pronta para aceitá-las. Enfileiramento significa que a latência vai crescer. Se a mensagem passar um segundo na fila, a latência de ponta a ponta será de pelo menos um segundo. O que é ainda pior, à medida que o tamanho da fila cresce, as latências aumentam gradualmente. Se o tamanho da fila não estiver vinculado, a latência pode exceder qualquer limite.


Observou-se que, embora a pilha de rede esteja sintonizada para a menor latência possível (o algoritmo de Nagle desligado, a interrupção da interrupção de NIC desativada, etc.), as latências ainda podem ser desanimadoras devido ao efeito de enfileiramento, conforme descrito acima.


Em tais situações, faz sentido iniciar lotes de forma agressiva. Não há nada a perder, pois as latências já são altas de qualquer maneira. Por outro lado, lotes agressivos melhoram o rendimento e podem esvaziar a fila de mensagens pendentes, o que, por sua vez, significa que a latência diminuirá gradualmente à medida que o atraso na fila diminuir. Uma vez que não há mensagens pendentes na fila, o lote pode ser desativado para melhorar ainda mais a latência.


Uma observação adicional é que o lote só deve ser feito no nível mais alto. Se as mensagens forem colocadas em lote, as camadas inferiores não terão nada para o lote de qualquer maneira e, portanto, todos os algoritmos de lotes abaixo não farão nada, exceto introduzir latência adicional.


Lição aprendida: Para obter o rendimento ideal combinado com o tempo de resposta ideal em um sistema assíncrono, desative todos os algoritmos de lotes nas camadas baixas da pilha e do lote no nível mais alto. Lote apenas quando novos dados chegarem mais rapidamente do que podem ser processados.


24,7. Visão geral da arquitetura.


Até este ponto nos concentramos em princípios genéricos que tornam o & Oslash; MQ rápido. De agora em diante, veremos a arquitetura atual do sistema (Figura 24.6).


Figura 24.6: Arquitetura do & Oslash; MQ.


O usuário interage com o & Oslash; MQ usando os chamados "sockets". Eles são muito semelhantes aos soquetes TCP, a principal diferença é que cada soquete pode manipular a comunicação com vários pares, um pouco como os soquetes UDP não acoplados.


O objeto de soquete vive no encadeamento do usuário (consulte a discussão sobre modelos de encadeamento na próxima seção). Além disso, o & Oslash; MQ está executando vários threads de trabalho que manipulam a parte assíncrona da comunicação: lendo dados da rede, enfileirando mensagens, aceitando conexões de entrada, etc.


Existem vários objetos que vivem nos threads de trabalho. Cada um desses objetos é propriedade de exatamente um objeto pai (a propriedade é denotada por uma linha completa simples no diagrama). O pai pode viver em um segmento diferente do filho. A maioria dos objetos é propriedade de soquetes; no entanto, há alguns casos em que um objeto pertence a um objeto que pertence ao soquete. O que temos é uma árvore de objetos, com uma árvore por soquete. A árvore é usada durante o desligamento; nenhum objeto pode se desligar até fechar todos os seus filhos. Dessa forma, podemos garantir que o processo de desligamento funcione conforme o esperado; por exemplo, que as mensagens de saída pendentes sejam enviadas para a rede antes de encerrar o processo de envio.


Grosso modo, existem dois tipos de objetos assíncronos; existem objetos que não estão envolvidos na passagem de mensagens e há objetos que são. O primeiro tem a ver principalmente com o gerenciamento de conexões. Por exemplo, um objeto ouvinte TCP recebe conexões TCP recebidas e cria um objeto engine / session para cada nova conexão. Da mesma forma, um objeto de conector TCP tenta se conectar ao ponto TCP e, quando obtém êxito, cria um objeto engine / session para gerenciar a conexão. Quando essa conexão falha, o objeto conector tenta restabelecê-lo.


Os últimos são objetos que estão manipulando a própria transferência de dados. Esses objetos são compostos de duas partes: o objeto da sessão é responsável por interagir com o soquete do Oslash; MQ, e o objeto do mecanismo é responsável pela comunicação com a rede. Há apenas um tipo de objeto de sessão, mas há um tipo de mecanismo diferente para cada protocolo subjacente & Oslash; o MQ suporta. Assim, temos mecanismos TCP, IPC (comunicação entre processos), mecanismos PGM (um protocolo multicast confiável, consulte RFC 3208), etc. O conjunto de mecanismos é extensível - no futuro, podemos optar por implementar, digamos, um WebSocket engine ou um mecanismo SCTP.


As sessões estão trocando mensagens com os soquetes. Existem duas direções para passar mensagens e cada direção é manipulada por um objeto pipe. Cada pipe é basicamente uma fila livre de bloqueio otimizada para transmissão rápida de mensagens entre threads.


Finalmente, há um objeto de contexto (discutido nas seções anteriores, mas não mostrado no diagrama) que mantém o estado global e é acessível por todos os sockets e todos os objetos assíncronos.


24,8. Modelo de Concorrência.


Um dos requisitos para o & Oslash; MQ era aproveitar as caixas multi-core; em outras palavras, dimensionar a taxa de transferência linearmente com o número de núcleos de CPU disponíveis.


Nossa experiência anterior com sistemas de mensagens mostrou que o uso de vários threads de uma maneira clássica (seções críticas, semáforos, etc.) não resulta em muita melhoria de desempenho. Na verdade, uma versão multiencadeada de um sistema de mensagens pode ser mais lenta que uma única, mesmo se medida em uma caixa multi-core. Segmentos individuais estão simplesmente gastando muito tempo esperando um pelo outro enquanto, ao mesmo tempo, obtêm muita troca de contexto que desacelera o sistema.


Tendo em conta estes problemas, decidimos optar por um modelo diferente. O objetivo era evitar o bloqueio total e permitir que cada thread fosse executado a toda velocidade. A comunicação entre os encadeamentos deveria ser fornecida por meio de mensagens assíncronas (eventos) transmitidas entre os encadeamentos. Isso, como os iniciados sabem, é o modelo clássico do ator.


A ideia era lançar um thread de trabalho por núcleo de CPU - ter dois threads compartilhando o mesmo núcleo significaria apenas muita troca de contexto para nenhuma vantagem em particular. Cada objeto interno & Oslash; MQ, como por exemplo, um mecanismo TCP, seria vinculado a um segmento de trabalho específico. Isso, por sua vez, significa que não há necessidade de seções críticas, exclusões mútuas, semáforos e afins. Além disso, esses objetos do & Oslash; MQ não serão migrados entre os núcleos da CPU, evitando assim o impacto negativo no desempenho da poluição do cache (Figura 24.7).


Figura 24.7: Vários threads de trabalho.


Este projeto faz com que muitos problemas tradicionais de multi-threading desapareçam. No entanto, há uma necessidade de compartilhar o thread de trabalho entre muitos objetos, o que, por sua vez, significa que deve haver algum tipo de multitarefa cooperativa. Isso significa que precisamos de um agendador; os objetos precisam ser orientados por eventos, em vez de estarem no controle de todo o loop de eventos; temos que cuidar de seqüências arbitrárias de eventos, mesmo que sejam muito raros; nós temos que ter certeza que nenhum objeto segura a CPU por muito tempo; etc.


Em suma, todo o sistema tem que se tornar totalmente assíncrono. Nenhum objeto pode fazer uma operação de bloqueio, porque ele não só bloquearia a si mesmo, mas também todos os outros objetos que compartilham o mesmo thread de trabalho. Todos os objetos precisam se tornar, explícita ou implicitamente, máquinas de estado. Com centenas ou milhares de máquinas de estado funcionando em paralelo, você precisa cuidar de todas as interações possíveis entre elas e, mais importante, do processo de desligamento.


Acontece que desligar um sistema totalmente assíncrono de maneira limpa é uma tarefa extremamente complexa. Tentar fechar mil partes em movimento, algumas delas funcionando, outras inativas, outras em processo de serem iniciadas, algumas delas já fechando sozinhas, são propensas a todos os tipos de condições de corrida, vazamentos de recursos e similares. O subsistema de desligamento é definitivamente a parte mais complexa do & Oslash; MQ. Uma verificação rápida do rastreador de erros indica que cerca de 30% a 50% dos erros relatados estão relacionados ao desligamento de uma forma ou de outra.


Lição aprendida: Ao buscar desempenho e escalabilidade extremos, considere o modelo do ator; é quase o único jogo na cidade em tais casos. No entanto, se você não estiver usando um sistema especializado como o Erlang ou o & Oslash; o próprio MQ, você terá que escrever e depurar muita infraestrutura manualmente. Além disso, pense, desde o início, sobre o procedimento para desligar o sistema. Essa será a parte mais complexa da base de código e, se você não tiver uma ideia clara de como implementá-la, provavelmente deverá reconsiderar o uso do modelo de agente em primeiro lugar.


24,9. Algoritmos Livres de Bloqueio.


Algoritmos livres de bloqueio têm estado em voga ultimamente. They are simple mechanisms for inter-thread communication that don't rely on the kernel-provided synchronisation primitives, such as mutexes or semaphores; rather, they do the synchronisation using atomic CPU operations, such as atomic compare-and-swap (CAS). It should be understood that they are not literally lock-free—instead, locking is done behind the scenes on the hardware level.


ØMQ uses a lock-free queue in pipe objects to pass messages between the user's threads and ØMQ's worker threads. There are two interesting aspects to how ØMQ uses the lock-free queue.


First, each queue has exactly one writer thread and exactly one reader thread. If there's a need for 1-to - N communication, multiple queues are created (Figure 24.8). Given that this way the queue doesn't have to take care of synchronising the writers (there's only one writer) or readers (there's only one reader) it can be implemented in an extra-efficient way.


Figure 24.8: Queues.


Second, we realised that while lock-free algorithms were more efficient than classic mutex-based algorithms, atomic CPU operations are still rather expensive (especially when there's contention between CPU cores) and doing an atomic operation for each message written and/or each message read was slower than we were willing to accept.


The way to speed it up—once again—was batching. Imagine you had 10 messages to be written to the queue. It can happen, for example, when you received a network packet containing 10 small messages. Receiving a packet is an atomic event; you cannot get half of it. This atomic event results in the need to write 10 messages to the lock-free queue. There's not much point in doing an atomic operation for each message. Instead, you can accumulate the messages in a "pre-write" portion of the queue that's accessed solely by the writer thread, and then flush it using a single atomic operation.


The same applies to reading from the queue. Imagine the 10 messages above were already flushed to the queue. The reader thread can extract each message from the queue using an atomic operation. However, it's overkill; instead, it can move all the pending messages to a "pre-read" portion of the queue using a single atomic operation. Afterwards, it can retrieve the messages from the "pre-read" buffer one by one. "Pre-read" is owned and accessed solely by the reader thread and thus no synchronisation whatsoever is needed in that phase.


The arrow on the left of Figure 24.9 shows how the pre-write buffer can be flushed to the queue simply by modifying a single pointer. The arrow on the right shows how the whole content of the queue can be shifted to the pre-read by doing nothing but modifying another pointer.


Figure 24.9: Lock-free queue.


Lesson learned: Lock-free algorithms are hard to invent, troublesome to implement and almost impossible to debug. If at all possible, use an existing proven algorithm rather than inventing your own. When extreme performance is required, don't rely solely on lock-free algorithms. While they are fast, the performance can be significantly improved by doing smart batching on top of them.


The user interface is the most important part of any product. It's the only part of your program visible to the outside world and if you get it wrong the world will hate you. In end-user products it's either the GUI or the command line interface. In libraries it's the API.


In early versions of ØMQ the API was based on AMQP's model of exchanges and queues. (See the AMQP specification.) From a historical perspective it's interesting to have a look at the white paper from 2007 that tries to reconcile AMQP with a brokerless model of messaging. I spent the end of 2009 rewriting it almost from scratch to use the BSD Socket API instead. That was the turning point; ØMQ adoption soared from that point on. While before it was a niche product used by a bunch of messaging experts, afterwards it became a handy commonplace tool for anybody. In a year or so the size of the community increased tenfold, some 20 bindings to different languages were implemented, etc.


The user interface defines the perception of a product. With basically no change to the functionality—just by changing the API—ØMQ changed from an "enterprise messaging" product to a "networking" product. In other words, the perception changed from "a complex piece of infrastructure for big banks" to "hey, this helps me to send my 10-byte-long message from application A to application B".


Lesson learned: Understand what you want your project to be and design the user interface accordingly. Having a user interface that doesn't align with the vision of the project is a 100% guaranteed way to fail.


One of the important aspects of the move to the BSD Sockets API was that it wasn't a revolutionary freshly invented API, but an existing and well-known one. Actually, the BSD Sockets API is one of the oldest APIs still in active use today; it dates back to 1983 and 4.2BSD Unix. It's been widely used and stable for literally decades.


The above fact brings a lot of advantages. Firstly, it's an API that everybody knows, so the learning curve is ludicrously flat. Even if you've never heard of ØMQ, you can build your first application in couple of minutes thanks to the fact that you are able to reuse your BSD Sockets knowledge.


Secondly, using a widely implemented API enables integration of ØMQ with existing technologies. For example, exposing ØMQ objects as "sockets" or "file descriptors" allows for processing TCP, UDP, pipe, file and ØMQ events in the same event loop. Another example: the experimental project to bring ØMQ-like functionality to the Linux kernel turned out to be pretty simple to implement. By sharing the same conceptual framework it can re-use a lot of infrastructure already in place.


Thirdly and probably most importantly, the fact that the BSD Sockets API survived almost three decades despite numerous attempts to replace it means that there is something inherently right in the design. BSD Sockets API designers have—whether deliberately or by chance—made the right design decisions. By adopting the API we can automatically share those design decisions without even knowing what they were and what problem they were solving.


Lesson learned: While code reuse has been promoted from time immemorial and pattern reuse joined in later on, it's important to think of reuse in an even more generic way. When designing a product, have a look at similar products. Check which have failed and which have succeeded; learn from the successful projects. Don't succumb to Not Invented Here syndrome. Reuse the ideas, the APIs, the conceptual frameworks, whatever you find appropriate. By doing so you are allowing users to reuse their existing knowledge. At the same time you may be avoiding technical pitfalls you are not even aware of at the moment.


24.11. Messaging Patterns.


In any messaging system, the most important design problem is that of how to provide a way for the user to specify which messages are routed to which destinations. There are two main approaches, and I believe this dichotomy is quite generic and applicable to basically any problem encountered in the domain of software.


One approach is to adopt the Unix philosophy of "do one thing and do it well". What this means is that the problem domain should be artificially restricted to a small and well-understood area. The program should then solve this restricted problem in a correct and exhaustive way. An example of such approach in the messaging area is MQTT. It's a protocol for distributing messages to a set of consumers. It can't be used for anything else (say for RPC) but it is easy to use and does message distribution well.


The other approach is to focus on generality and provide a powerful and highly configurable system. AMQP is an example of such a system. Its model of queues and exchanges provides the user with the means to programmatically define almost any routing algorithm they can think of. The trade-off, of course, is a lot of options to take care of.


ØMQ opts for the former model because it allows the resulting product to be used by basically anyone, while the generic model requires messaging experts to use it. To demonstrate the point, let's have a look how the model affects the complexity of the API. What follows is implementation of RPC client on top of a generic system (AMQP):


On the other hand, ØMQ splits the messaging landscape into so-called "messaging patterns". Examples of the patterns are "publish/subscribe", "request/reply" or "parallelised pipeline". Each messaging pattern is completely orthogonal to other patterns and can be thought of as a separate tool.


What follows is the re-implementation of the above application using ØMQ's request/reply pattern. Note how all the option tweaking is reduced to the single step of choosing the right messaging pattern (" REQ "):


Up to this point we've argued that specific solutions are better than generic solutions. We want our solution to be as specific as possible. However, at the same time we want to provide our customers with as wide a range of functionality as possible. How can we solve this apparent contradiction?


The answer consists of two steps:


Define a layer of the stack to deal with a particular problem area (e. g. transport, routing, presentation, etc.). Provide multiple implementations of the layer. There should be a separate non-intersecting implementation for each use case.


Let's have a look at the example of the transport layer in the Internet stack. It's meant to provide services such as transferring data streams, applying flow control, providing reliability, etc., on the top of the network layer (IP). It does so by defining multiple non-intersecting solutions: TCP for connection-oriented reliable stream transfer, UDP for connectionless unreliable packet transfer, SCTP for transfer of multiple streams, DCCP for unreliable connections and so on.


Note that each implementation is completely orthogonal: a UDP endpoint cannot speak to a TCP endpoint. Neither can a SCTP endpoint speak to a DCCP endpoint. It means that new implementations can be added to the stack at any moment without affecting the existing portions of the stack. Conversely, failed implementations can be forgotten and discarded without compromising the viability of the transport layer as a whole.


The same principle applies to messaging patterns as defined by ØMQ. Messaging patterns form a layer (the so-called "scalability layer") on top of the transport layer (TCP and friends). Individual messaging patterns are implementations of this layer. They are strictly orthogonal—the publish/subscribe endpoint can't speak to the request/reply endpoint, etc. Strict separation between the patterns in turn means that new patterns can be added as needed and that failed experiments with new patterns won't hurt the existing patterns.


Lesson learned: When solving a complex and multi-faceted problem it may turn out that a monolithic general-purpose solution may not be the best way to go. Instead, we can think of the problem area as an abstract layer and provide multiple implementations of this layer, each focused on a specific well-defined use case. When doing so, delineate the use case carefully. Be sure about what is in the scope and what is not. By restricting the use case too aggressively the application of your software may be limited. If you define the problem too broadly, however, the product may become too complex, blurry and confusing for the users.


24.12. Conclusão.


As our world becomes populated with lots of small computers connected via the Internet—mobile phones, RFID readers, tablets and laptops, GPS devices, etc.—the problem of distributed computing ceases to be the domain of academic science and becomes a common everyday problem for every developer to tackle. The solutions, unfortunately, are mostly domain-specific hacks. This article summarises our experience with building a large-scale distributed system in a systematic manner. It focuses on problems that are interesting from a software architecture point of view, and we hope that designers and programmers in the open source community will find it useful.


zeromq.


The financial sector lives off messaging technology. On "Wall Street" (the global stock trading business), capacity and latency are everything. Current infrastructure, highly tuned to get million-message per second throughputs, and sub-millisecond latencies, still fails when trading gets frantic. Huge amounts of money depend on being the first to get data, and the first to trade.


The stock trading business is evolving dramatically. Penny pricing generates more data. New US and EU regulations increase the number of parties involved in financial markets. New algorithmic trading technologies increase the demand for up-to-date stock data and icrease number of orders. While the existing infrastructure can double in capacity or speed per 18 months, traffic is expected to grow by 20 times over the next three years 1 .


In the same time, prices for messaging technology are steadily rising. Messaging middleware - software that connects applications or pieces of applications in a generalised plug-and-play fashion - is one of the last big-ticket items still not turned into a commodity by the Internet age of cheap software.


Mainframes got much of their power from clever messaging, transaction processing systems like IBM CICS. But today even 1980's-standard middleware - unlike databases, operating systems, compilers, editors, GUIs, and so on - is still not widely available to ordinary developers. The software industry is producing various business applications and pieces of applications, and the tools to make these, in ever greater quantities, and ever lower prices, but the messaging bit is still missing. The lack of a way to connect these applications has become not just an unconquered terrain, but also a serious bottleneck to growth, especially for new start-ups that could in theory compete aggressively with larger, older firms, if they were able to cheaply combine existing blocks of software.


This frustration is visible in many markets and has lead to the growth of messaging-over-HTTP (SOAP), and other compromises. Architectures like SOAP do work, but they don't solve the two main issues of a enterprise-level messaging, namely routing and queuing. Thus businesses who use such technologies cannot scale, and cannot compete in really large markets, unless they write their own messaging software, or buy a commercial product. Various other standardisation attempts were made to commoditise the market: CORBA, JMS and lately AMQP, CORBA being unsuccessful because of RPC metaphor that doesn't suit the needs of financial markets, JMS succeeding in Java world, but unable to expand any further and AMQP still being a big unknown.


The increasing demand, and lack of real competition shows in the financial statements of high-end messaging vendors like Tibco Software Inc: "Total revenue in the first quarter of fiscal year 2007 compared to the same quarter last year increased by $11.0 million or 10%. The increase was comprised of a $7.0 million or 11% increase in service and maintenance revenue and by a $4.0 million or 8% increase in license revenue." 2 Tibco customers report that license fees are increasing, year on year.


O mercado.


The global stock trading market is primary focus of ØMQ because that's where most emphasis is placed on messaging, most resources are accumulated and most edge-cutting technologies are used.


The main characteristic of the market is hunger for fast delivery. Every millisecond the stock quote or the trade order is faster than the competing one translates into direct financial profit, so the firms involved are naturally eager for any advantage they can get.


Currently, in the stock-trading business traffic load is so high and latency so critical, that the middleware has to be highly optimised. Latencies are given in microseconds and throughputs in millions of messages per second… In spite of that, trading often experiences problems when message load peaks. Latency can suddenly drop to seconds (or even tens of seconds) and huge amounts of money can be lost as trades are delayed or fail. 3


The situation is getting worse for several reasons:


In 2001, the NYSE and NASDAQ switched from pricing their stocks in 1/16th dollar units to single cent units. This so-called "penny pricing" means stock markets produce more data and this data must be shifted across networks. Both in the US and EU, regulators are forcing financial markets to compete more openly and aggressively, in the interests of consumers. For example US SEC regulatory changes allow new firms to act as intermediaries in the stock trading sector while the EU's Markets in Financial Instruments Directive (MiFID) 4 is expected to increase stock-trading traffic rates in EU to match the volumes seen in US after Reg NMS 5 . Many new and aggressive firms are entering the market, especially building or using 'algorithmic trading' platforms. Algorithmic trading executes big amount of low-volume orders as opposed to small amount of high-volume orders executed by traditional human traders.


So we have increased data flows, to more participants, who are pushing to develop new business models which depend on getting that data rapidly, detecting temporary market anomalies, and responding to it (with trades) before their competitors. A more flexible regulatory environment is opening previously protected markets to new competition. Overall, we see an arms race for bandwidth and latency in which better technology translates directly into more profits. 6


Message traffic is expected to grow significantly in the near term - we have heard different figures of up to 30 times over the next three years - and existing systems can only double capacity every 18 months.


There are many attempts to solve this emerging issue. The most dramatic improvements in performance come from replacing the classic central broker with a peer-to-peer architecture in which messages can flow directly across the network with no extra hops. Not all messaging systems can adapt their architecture in this way.


Apart from architecture, the obvious place to optimise messaging is in the "stack", i. e. the layers that separate the application program from the physical network. The software itself is already heavily optimised in most cases, so vendors are shifting to other options, such as:


Optimising network architecture by connectivity providers to get better latencies, including moving message consumers close (in network terms) to the message producers; 7 , 8 . Clients moving from consolidated stock quote feeds to direct connectivity to the exchanges; 9 Optimising formats in which data are passed (FIX/FAST 10 ); Providing full-blown hardware solutions (Tervela, Exegy, etc.); Replacing the physical transport layer (Infiniband 11 , 10GB Ethernet); Optimising existing networking hardware. TCP offload engines, Intel's I/OAT technology; 12 , etc.; Modifying the OS to handle the messages in real-time. Various real-time OS's, like Novell's SLERT 13 ; Modifying the OS to use more efficient messaging stack: Asynchronous I/O, SDP, various zero-copy techniques etc.; Using multicast to distribute stock quotes on the client's LAN;


As well as these optimisations, which focus on individual aspects of the messaging stack or architecture, we also see attempts that look at the problem as a whole:


Intel's Low Latency Lab 14 Securities Technology Analysis Center (STAC) 15 Various measurement & monitoring solutions (Endace etc.)


Highly optimised products with extensive hardware support become very expensive. Only the largest trading firms can afford the full range of products and even for these firms, costs remain a persistent concern. For the smaller firms, many of the solutions are simply not an option.


Oportunidades


In this section we look at the opportunities for new high-performance messaging products such as those we are building.


High-performance take-out.


The first and most obvious target is any firm using high-end commercial middleware for stock trading, where we can provide a cheaper equivalent. This market is cost-sensitive and in our experience it is willing to absorb change and risk in order to get a compelling price and/or performance advantage over their competitors.


Further, there are many firms who cannot afford these products, but would use them if the cost was set lower. Zipf's Law (usually used for language but also applicable to business sizes) suggests that the number of firms and their size follows an inverse power ratio, so offering a product at 20% the price of the high-cost market leaders should open a market five times as large. (In fact it's probably not this large, because smaller firms will buy or rent trading platforms rather than try to build their own.)


Plataformas de negociação.


Trading platforms are software applications that trading firms can buy ready-made, rather than build themselves using messaging middleware. Given the demand for cheaper, faster trading, there is a large market for these platforms. Obviously a firm that builds a trading platform is sensitive to the cost of the messaging it uses and these firms provide a market for our planned products.


Investment banks.


Investment banks build their own trading systems and (from our limited experience) like to have control over the technology they use. Standards-based systems are highly attractive here. The calculation is that a standard technology is easier to control, and is served by a larger market of cheaper experts. Any AMQP solution has immediate attraction. Cost is always a driver as well but for firms that do significant development around the messaging, reduction of secondary costs (such as the number and cost of in-house consultants) is an important aspect.


It becomes clear why JPMorganChase was motivated to push and invest in the AMQP process, even taking considerable risks at the time: AMQP enables very large savings on IT expenditure, for messaging licenses, custom development, operational control, and so on. We can deliver a much lower-risk proposal to other investment banks, but with the same kinds of benefits.


Data consolidators.


The stock trading world connects many exchanges (NASDAQ, NYSE, etc.) to many clients. Large clients make separate connections to each exchange, but most work via data consolidators, firms like Reuters who provide unified streams from many sources.


Today's consolidators run highly-tuned custom messaging software, it is not standards-based, and has little scope for getting cheaper and faster. It can get faster, but only at high cost, which punishes those firms that stick with custom messaging, and gives an advantage to those firms using standards-based messaging, which spreads the costs and leverages far more work on performance.


There is a definite opportunity for opening this market, and allowing new firms to compete as data consolidators, using our high-performance products to carry quotes to clients. New US regulations are opening this market to real competition.


The exchanges (stock exchanges, currency exchanges, commodities, etc.) are heavily impacted by the growth in demand for their services. It seems inevitable that standards at the edges will slowly force their way into the center, and we should be able to follow with product offerings.


Also, new types of trading venues are emerging (ATS's, MTF's and dark pools 16 ) that gradually take still greater share of the market from the traditional exchanges. Given that this trend is quite new and still gaining momentum, we expect to see increasing demand for high-end messaging systems on this market.


Moving the value to different markets.


One of the goals of ØMQ is to use money, resources and experience accumulated during low-latency arms race in stock-trading business to deliver free high-end general-purpose messaging solution to the rest of IT sector.


Some of the areas where ØMQ may prove useful follow.


Business and institutional messaging.


Sending payments, doing business-to-business communication, passing documents within governmental organisations etc. is the primary market to focus on apart of stock trading. The reason is that this is the field where messaging is used traditionally, with lot of experienced IT personel aware of messaging and using it for a long time.


It should be also taken into account that even applications that don't use messaging proper may be still sending 'messages' by different means. Consider an application located at place A writing a record to remote database server and another one at place B reading the record. In fact, there was a message sent from A to B, even though the programmer might not be aware of it. Even inter-process and inter-thread communication can be considered messaging. Synchronising different applications by copying files to remote destinations once a day can be considered messaging as well (although it is a spectacularly low-latency one).


Basically any application made for financial or institutional sector needs some kind of messaging and the cost of the implementation varies between 10 and 30 per cent of the total project cost, so using existing standards-based middleware implementation seems to be a rather good investment.


Although low latency is not a key requirement in this sphere, we expect that growing transaction rates (consider regulations like EU's SEPA 17 and standardisation efforts like TWIST 18 ) will slowly force financial institutions to adopt high-performance messaging solutions, thus causing the current small slice of the messaging market addressed by high-performance solutions will steadily grow, until it ultimately reaches 100%.


Embedded systems.


Embedded systems often have real-time requirements similar to those seen in stock-trading business. Consider, for example, an equipment measuring some critical value in a technological process. The data have to be delivered to the unit controlling the process within 1 ms, otherwise the whole process will be spoiled.


Embedded systems don't usually need the throughput provided by stock-trading stacks, however, if the latency, reliability and deterministic delivery times are guaranteed, they can take advantage of it, even though it doesn't use all the bandwidth capacity available.


Multimídia.


Same remark about real-time requirements applies to multimedia (streaming audio and video, teleconferencing, etc.). As opposed to embedded systems, latency is not that critical, the paramount being deterministic delivery time and high throughput.


In the future we may find out that lot-of-small-messages model of stock-trading apps is incompatible with stream-based multimedia approach. However, we don't believe this is the case. To test the hypothesis, we've built proof-of-concept teleconferencing application over AMQP and we've seen it perform smoothly.


Grid computing.


Having almost the same requirements as stock trading, grid systems are natural area to employ ØMQ stack.


Grids are icreasingly being used in financials 19 and - not surprisingly - in stock trading itself, providing a solution for computationally expensive problems like risk management and algorithmic trading 20 .


The low-latency bubble.


The market for low-latency solutions is very lively and expanding these days. However, some have a feeling that the value of the market is overestimated and that low-latency arms races going on will result in the burst of the bubble, similar to dot-com crash of early 2000's.


Let's examine possible causes of market breakdown:


There are law's of physics that place lower bound on the latency. Specifically, speed of light cannot be exceeded and once the messaging hits this limit, there won't be much space for competition and low-latency arms race will come to its end. The costs for fast messaging are constantly growing. Once we hit the point where improving the latency will require investments exceeding the profits it can possibly yield, the flow of money into the market will end. Unreasonable spending on low-latency solutions can result in hysteria, once the still growing low-latency market starts shrinking. Hysteria can make the market plummet even below it's real value.


Our view of the problems above is following:


Speed of light is certainly an ultimate barrier, however, as can be seen with microprocessors, barriers seen as ultimate are quite prone to be crossed over and over again. In messaging business for example, we see emerging proximity solutions (handling speed of light problem by placing interdependent applications physically close one to another) or the trend to optimise software part of the messaging stack thus removing endpoint latency rather than on-the-wire latency. In fact, we don't believe there are any real unpenetrable barriers to stop low-latency arms race at least in the next several years. Although costliness of the low-latency messaging grows steadily, it should be taken into account that technology price - both hardware and software - is steadily decreasing at the same time. What cost $100 last year, costs $50 today. So, even in stable, non-expanding market, where spending on IT keeps constant, there will be a demand for new solutions to keep pace with new technologies. Hysteria can happen at any time and there's no way to prevent it completely. However, as stock-trading messaging is in a way a world for itself, we expect hysteria to be restricted to this turbulent little market leaving the rest of messaging market intact. Thus the main victims will be the firms that provide specialised stock-trading solutions rather than general-purpose messaging. Specifically, ØMQ project, by taking advantage of the resources accumulated in stock-trading-focused IT market to develop general-purpose messaging solution can survive market breakdown by relying on its presence in different sectors of messaging market.


Conclusão.


The primary focus of ØMQ starts with stock trading because this market has a well-defined and growing demand for high-end solutions, and the options for collaborations and return on investment are plentiful. However, the construction of a cost-efficient, standards-based messaging system that can compete head-on with the best in the world opens doors into many other domains as well.


Comments: 0.


If you found this page useful, please rate it up so others will find it.


Who's watching this page?


Web site design and content is copyright (c) 2014 iMatix Corporation. Contact us for professional support. Site content licensed under cc-by-sa 3.0 ØMQ is copyright (c) Copyright (c) 2007-2014 iMatix Corporation and Contributors. ØMQ is free software licensed under the LGPL. ØMQ and ZEROMQ are trademarks of iMatix Corporation. Terms of Use — Política de Privacidade.


Python for developing a real-time automated trading platform.


Abstract—Python, nowadays, seems like the perfect environment for developing a real-time automated trading tool. In this talk we will talk about how we have developed: a general-purpose multiagent-system module using Pyro and ZeroMQ; a platform, based on it, for developing automated trading strategies using Numpy, Numba, Theano. ; and a tool for visualizing real-time market data using PyQtGraph and Qt.


Index Terms—Python, ZeroMQ, multi-agent, Pyro, NumPy, Numba, Theano, PyQtGraph, Qt.


I. A MULTI - AGENT SYSTEM.


The architecture of any system can vary and still perform the same task. A monolithic architecture is best when seeking for performance but, on the other hand, division provides more robustness in case a single module fails and allows making modifications without the need to compile (if this is the case) the whole infrastructure. Furthermore, when talking about computational intensive tasks, where the ratio between data transmission and computation time is very low, a module-based architecture barely impacts the overall performance.


Also, a module-based architecture allows the creation of scallable, distributable, highly-available and parallel systems. A multi-agent system is composed of multiple interacting agents trying to solve problems that are difficult for an individual agent. The main characteristics are:


• Autonomy: the agents are at least partially independent, self-aware and autonomous.


• Local views: no agent has a full global view of the system.


• Decentralization: there is no designated controlling agent.


In OpenSistemas we have developed a general-purpose multi-agent system which is written in pure Python: osBrain.


Each agent is a system process spawned using the multiprocessing module, meaning that it runs independently from the others and that it does not hit performance issues when using GIL-enabled Python interpreters.


This system process starts a Pyro server and registers itself to the name server. The Pyro server is used to serve an object: an instance of the actual agent, which is described bellow. This implementation allows the user to access the object through a Pyro proxy, treating the agent, which could be on a remote machine, as a local object and being able to change its attributes and its behavior.


While Pyro is not the most efficient way for communication between processes, it is very convenient for deployment, allowing the creation of complex, distributed multi-agent systems in a simple way.


Agents, however, communicate with each other using ZeroMQ. ZeroMQ is more efficient and very flexible, allowing the user to define different communication patterns based on their needs. A typical agent process will be running a Pyro server in which the main thread runs a loop that simply awaits for incomming messages from the outside. This behavior can be of course modified at will but would be definitely the most common case.


Agents can use multithreading as well and are provided with an inproc socket polled by the main thread to ensure safe access to memory even on GIL-disabled Python interpreters.


II. A REAL - TIME AUTOMATED TRADING PLATFORM.


In OpenSistemas have developed a broker-independent platform for real-time automated trading: osMarkets. Any broker can provide the necessary data and and the platform will perform all the necessary computations to produce orders that will be sent back to the broker to be executed.


This platform is implemented over osBrain but having specialized agents.


Feeder is an agent which receives real-time data from the broker. It typically uses multithreading and the loopback socket in order to be able to stream tick market data while being able to send active requests to the broker (eg. request for historical data). It also converts the data so that it can be in the adequate format for the rest of the platform.


Router is an agent which receives data from feeders. It manages the historical data and the creation of new bars using real-time tick data. Router distributes updates on the market data to all the interested agents in the network.


Brain is the most common agent. Is receives data from router or from other brains and processes it, sending the results to other brains or sending orders to be executed. This is where the automated trading strategies are to be implemented. Brains can make use of many useful packages avilable in the Python ecosystem: NumPy, SciPy, Numba, Theano. só para citar alguns. Brains can form a hierarchy that can be used to abstract market data.


Trader is an agent which is designed to interact with the broker, just as the feeder, but to execute market orders (i. e.: buy/sell). Other parameters such as stop-loss or take-profit can be handled by the broker or internally by osMarkets as well.


In order to manage market data, NumPy ndarrays are being used. When working with real-time data, time series are always changing. In order to avoid full memory copies on each update, we have created a custom class which uses a bigger structure as a buffer. This buffer, which is an actual NumPy ndarray is filled and or modified on update and the custom class simply updates the view of if in memory.


III VISUALIZING REAL - TIME MARKET DATA.


While Matplotlib is probably the most well-known tool for data visualization and while it is very good at displaying all kind of graphics with very good quality, it is not suitable for real-time visualizations and not very good at interaction.


PyQtGraph, on the other hand, is a great tool for real-time visualization and for interactive graphics. It is written in pure Python, so installing this package is pretty straight-forward.


Underneath it uses Qt and OpenGL to allow fast displaying and interactions.


While it is still in its earliest stages, we are developing a tool for real-time visualization of trading strategies using PyQtGraph. This tool acts as an agent in the multi-agent system, meaning that it simply subscribes to updates on market data to router and on the outputs of selected brains.


It is able to draw candlestick charts and basic indicators and allows the user to handle brains from the interface.


Using AMQP from Delphi with ZeroMQ.


The defining features of AMQP are message orientation, queuing, routing (including point-to-point and publish-and-subscribe), reliability and security .


The good news about AMQP is that AMQP mandates the behaviour of the messaging provider and client to the extent that implementations from different vendors are truly interoperable, in the same way as SMTP, HTTP, FTP, etc. have created interoperable systems.


In a so “Open” market, live an interesting project called ZeroMQ.


In a my recent Delphi project, I must choice a thin and fast messaging system, ZeroMQ has been the choice.


However, ZeroMQ has not the Delphi client for talking with the broker, so I decided to write my own.


ZeroMQ is very fast but doesn’t support some enteprise features like users management and message persistence, but is very simple to use and to intergate in a legacy system.


For example, with my wrapper, a simple “sender” is like following:


And a simple receiver is simple as follow:


In the distribution there are a complete set of examples including a simple “Chat” aplicação.


ZeroMQ is primarily intended to power stock trading business, this is the reason becouse is very fast.


To use ZeroMQ you need the ZeroMQ server downloadable from zeromq/ where you can find additional info about Exchange and Queue configuration and binding.


Wrapper (beta) can be downloaded from the ZeroMQ section.


Comments and fix for the wrapper are very apreciated.


Have fun and happy messaging 🙂


Pós-navegação.


8 pensamentos sobre & ldquo; Using AMQP from Delphi with ZeroMQ ”


Which is the license for the code ?


Would be interesting to have a project at sourceforge/GoogleCode to help improve the code.


Great work, thanks !


promising approach. Sad to see there’s no MPL license available for it. LGPL will create problems when linking the library statically into commercial Delphi Applications. Only solution so far: put the message queue code into a DLL and do a late binding of it while publishing the DLL code again under LGPL.


This example actually works?


You are using GlobalQueue and LocalQueue, shouldn’t it be the same?


In my test program this code hangs on ‘zmq. Receive’.


Please let me know if you want port of this library for Freee Pascal Compiler.


Are You trying to port to Freepascal ? I have converted the code to test with Lazarus on Ubuntu.


When I try to run the Chatroom Example on my Ubuntu (Hardy 64 bits) the entire X Freezes. : D


Your wrapper included with the ZeroMQ distro is broken.


Take a look at the open method, it uses Fhost, but it is never set anywhere. you should override the create method and set fhost there, or just get rid of Fhost and change the open method to use the host var that is passed.


After fixing the open method it still does not work and raises a access violation when the open method is called.


ZeroMQ – How To Interface Python/R with MetaTrader 4.


ZeroMQ – Distributed Messaging.


In this post, we present a technique employing ZeroMQ (an Open Source, Asynchronous Messaging Library and Concurrency Framework) for building a basic – but easily extensible – high performance bridge between external (non-MQL) programming languages and MetaTrader 4.


Reasons for writing this post:


Lack of comprehensive, publicly available literature about this topic on the web. Traders have traditionally relied on Winsock/WinAPI based solutions that often require revision with both Microsoft™ and MetaQuotes™ updates. Alternatives to ZeroMQ include named pipes, and approaches where filesystem-dependent functionality forms the bridge between MetaTrader and external languages.


Click here to watch the Webinar Recording.


In this blog post, we lay the foundation for a distributed trading system that will:


Consist of one or more trading strategies developed outside MetaTrader 4 (non-MQL), Use MetaTrader 4 for acquiring market data, trade execution and management, Support multiple non-MQL strategies interfacing with MetaTrader 4 simultaneously, Consider each trading strategy as an independent “Client”, Consider MetaTrader 4 as the “Server”, and medium to market, Permit both Server and Clients to communicate with each other on-demand.


Infographic: ZeroMQ-Enabled Distributed Trading Infrastructure (with MetaTrader 4)


Why ZeroMQ?


Enables programmers to connect any code to any other code, in a number of ways. Eliminates a MetaTrader user’s dependency on just MetaTrader-supported technology (features, indicators, language constructs, libraries, etc.) Traders can develop indicators and strategies in C/C#/C++, Python, R and Java (to name a few), and deploy to market via MetaTrader 4. Leverage machine learning toolkits in Python and R for complex data analysis and strategy development, while interfacing with MetaTrader 4 for trade execution and management. ZeroMQ can be used as a high-performance transport layer in sophisticated, distributed trading systems otherwise difficult to implement in MQL. Different strategy components can be built in different languages if required, and seamlessly talk to each other over TCP, in-process, inter-process or multicast protocols. Multiple communication patterns and disconnected operation.


ZeroMQ: Supported Programming Languages.


Though we focus on MQL interfaced with Python & R in this post, the basic process described here can be implemented easily in other ZeroMQ-supported languages.


A comprehensive list of ZeroMQ language bindings is available here:


Who else is using ZeroMQ?


AT&T, Cisco, EA, Los Alamos Labs, NASA, Weta Digital, Zynga, Spotify, Samsung Electronics, Microsoft, CERN and Darwinex Labs.


ZeroMQ also powers at least 5 DARWINS on The DARWIN Exchange, where the underlying trading strategies were written in C++, Python and R.


Planning Flow Control.


This post is not intended to be a detailed tutorial on ZeroMQ .


However, it is still important to understand a few things about ZeroMQ that make it particularly suited to the task of connecting external programming languages such as Python and R to MetaTrader 4.


It supports TCP, inter-process, in-process, PGM and EPGM enabled multicast networking. We will use the TCP transport type for the implementation in this post. ZeroMQ enables servers and clients to connect “to each other” on demand, particularly useful for designing distributed trading infrastructure. In addition to support for asynchronous communication and disconnected operation, ZeroMQ supports several communication patterns that permit higher-level data transfer, freeing programmers to focus more on the transfer logic rather than low-level mechanisms. These patterns include: Request (REQ) / Reply (REP), Publish (PUB) / Subscribe (SUB) and Push (PUSH) / Pull (PULL).


For the implementation in this blog post, we will employ ZeroMQ’s REQ/REP and PUSH/PULL communication patterns. MetaTrader 4 will be our “Server”, and trading strategies will be “Clients”.


Please note that this (MT4=Server, Strategy=Client) is not a MUST – you will need to decide on whatever flow control suits your particular needs best.


For example, you might designate a machine independent of both the trading strategy as well as MetaTrader 4, as your Server , and have Strategies and MT4 both be Clients . There are a number of ways you could achieve the end goal; carefully planning flow control will lead to efficient functionality.


Request (REQ) / Reply (REP) Pattern.


The Server (MetaTrader 4 EA) will employ a TCP socket of type REP , to receive requests and send responses. A REP socket MUST always initiate a pair of calls : first, a receive, followed by a send.


The Client (Trading Strategy, e. g. in Python) will employ a TCP socket of type REQ , to send requests and receive responses. A REQ socket MUST always initiate a pair of calls too : first, a send, followed by a receive.


For this implementation, the REQ/REP pattern will enable our Clients to send commands to the MetaTrader 4 Server and receive acknowledgements of the same (e. g. OPEN/MODIFY/CLOSE trades, GET BID/ASK RATES, GET HISTORICAL PRICES, etc.)


Push (PUSH) / Pull (PULL) Pattern.


The Server (MetaTrader 4 EA) will also employ a second, PUSH socket, to send additional information to Clients (Trading Strategies). This is a one-way socket, and the server will only be able to send data to this socket, without being able to receive anything back through the same socket.


The Client (Trading Strategy) will also employ a second, PULL socket, to receive additional information from the Server. This too is a one-way socket, and the client will only be able to receive data from this socket, without being able to send anything through the same socket.


The PUSH/PULL pattern enables servers and clients to exchange data with each other on-demand, but in one direction without expecting a response. This could of course be swapped out for another REQ/REP pattern, depending on your application’s flow control requirements.


In summary, for this post’s basic implementation:


The Server will employ two sockets, one REP and one PUSH. Each Client will employ two sockets, one REQ and one PULL.


Infographic: What this flow control plan looks like in practice.


MetaTrader 4 Expert Advisor – Componentes


As displayed in the infographic above, the MT4 EA will serve as our ZeroMQ-enabled Server, with three main modules:


MESSAGE ROUTER – This allows the EA to receive commands and send acknowledgements back to connecting Clients (trading strategies) through the REP socket. The Router passes all messages on to the Parser. Note: For this example, the Router doesn’t serve much purpose, but it is good practice to have this intermediary where several strategies connect to the Server (MT4) and some manner of pre-parse actions may need to be performed. MESSAGE PARSER – Messages received by this module are decomposed into actions for the next module (Interpreter & Executor). INTERPRETER & EXECUTOR – This module literally “interprets” decomposed messages and performs requested actions accordingly. For example, if the Client is requesting market data, the module gathers it from the MetaTrader 4 History DB and sends it on to the Client via the PUSH socket. Alternatively, if the Client is requesting a BUY or SELL trade be opened on e. g. the EUR/USD, it sends the trade to market and a notification of success/failure/ticket-info to the Client via the PUSH socket.


Implementation Requirements.


ZeroMQ – MQL4 Bindings -> Download and install the required files as instructed here: github/dingmaotu/mql-zmq For Python -> “pyzmq” library For R -> “rzmq” library.


Sample Code.


To give you a head start, we’ve published a functional MetaTrader 4 Expert Advisor with the full implementation discussed in this blog post.


The MQL sample code provided is quite extensible, and can be used as a template in your efforts.


The Python and R samples demonstrate how communication patterns are implemented. It’s fairly simple to integrate this code in your existing Python/R trading strategies.


Webinar Recording: How to Interface Python/R Trading Strategies with MetaTrader 4.


In this third installment of our ZeroMQ series, we describe how to use ZeroMQ in non-MQL trading strategies to get the following information: Account Information (e. g. equity, margin, balance, etc) Trades at market (live or pending) Historical Trades If you haven’t already, please consider reading the following posts before proceeding further in this article: ZMQ-I: […]


This post builds on the contents of the previous article in this series, namely ZeroMQ – How to Interface Python/R with MetaTrader 4. Therein, we proposed a solution to creating trading strategies in ZeroMQ supported programming languages outside the MetaTrader environment, with the latter simply acting as the intermediary to the market. Leveraging ZeroMQ’s convenient […]


This post describes how to setup a data science environment for DARWIN R&D. Whether you’re a Data Scientist, Quant, Trader, Investor, Researcher, Developer or just someone keen on putting the DARWIN asset class under a scientific microscope, the contents of this post should hopefully give you a sound start. The tools, libraries and datasets referenced herein […]


In this post, we describe the purpose, composition, parity, calculation logic and construction of a Currency Index for each of the 8 major currencies. These include: EUR – Euro USD – US Dollar GBP – British Pound JPY – Japanese Yen AUD – Australian Dollar NZD – New Zealand Dollar CHF – Swiss Franc CAD […]

No comments:

Post a Comment