Sugestões de mensageria ou distribuição de processamento

18 respostas
P

Ola

Hoje eu trabalho com um sistema bem simples (hoje perl, sendo reescrito em ruby). Dado um input de um usuário eu crio um arquivo em um diretório X (cujo nome é um “UUID” dessa requisição) e esse diretório é processado por vários módulos.

Cada modulo é um serviço que fica olhando para um diretorio (indir) e, quando chega alguma coisa, move pra um ‘workdir’ e, terminado o processamento, move para um ‘outdirque pode ser o indir do proximo módulo.

É uma fila implementada em um filesystem, basicamente, “controlado” por um modulo central via pequenas requisições a uma api REST. O sistema é muito simples (e robusto) porém é fortemente baseado em I/O e funciona redondo para sistemas sequenciais onde tenho que fazer os processamentos A -> B -> C -> …Z .

Eu não queria perder a simplicidade mas existe um requisito de paralelizar algumas operações em maquinas distintas e ai fazer fila em filesystem se torna um inferno, pois tenho montagem NFS e minhas operações de movimentação deixam de ser atômicas, por exemplo. Nesse caso eu quero passar para algo como

A -> ( B, C , D ) -> E

onde o que esta entre parentesis deve ser feito em paralelo - em maquinas fisicas diferentes. Tenho outro requisito que é lidar com prioridade de fila, que hoje é complicado pois cada modulo funciona “quando pode”.

Pergunta:
Existe alguma coisa que seja simples (no sentido de ter poucas dependências, para ter poucos pontos de falha) de implementar que me dê estas caracteristicas? Pensei em SOAP sobre XMPP ao invés de criar algo em um diretório mas não sei se existe algo mais simples.

Talvez alguma mensageria de forma mais leve, não sei. Aceito sugestões.

18 Respostas

B

Bem, tens um sistema simples e robusto, quer paralelizar partes deles, e ainda manter a nova implementação com poucas dependências, e ainda super simples. Vou tentar trabalhar com o que já existe:

Esse compatilhamente via NFS realmente está te atrapalhando? Não funciona em paralelo ou é somente uma suposição? Eu sei que pode acontecer de dois serviços lerem o mesmo arquivo ao mesmo tempo, mas acontece ao tirar o arquivo de um lugar e mover para outro? O proprio sistema operacional deveria tratar dessa movimentação atômicamente.

Outra pergunta, A -> ( B, C , D ) -> E significa

A -> ( B -> C -> D) -> E , em parenteses sendo a operação sendo executada em paralelo ou
A -> (B) -> E, A -> © -> E, A -> (D) -> E, ou uma mistura disto?

Possivel solução:

Se não puder fazer com que a máquina 2 pegue os arquivos diretamente do indir1 remoto e coloque no workdir2 local, que tal a máquina 2 pedir para a máquina 1 colocar o arquivo lá? Seria uma inversão do controle da movimentação dos arquivos, onde a máquina 1 teria uma fila interna(muito simples, sem recorrer a mensageria) para atender os pedidos.

M

Voce diz que o requisito é distribuir o processamento para diferentes máquinas. Mas eu duvido muito que esse seja o requisito, acho que o verdadeiro requisito vc omitiu da gente. Algo como fazer o serviço mais rápido, ou ser tolerante a piques de luz na maquina responsavel pelo processamento, etc.

Se realmente a arquitetura precisa passar a ser distribuida e o filesystem realmente não der conta vc pode adotar uma arquitetura de queue distribuída, como isso: http://aws.amazon.com/sqs/

M

Simples e distribuído simplesmente não existe.

P

ok, tenho uma exigencia de SLA de alguns minutos

ao inves de fazer

a -> b -> c -> d

onde b e c demoram muito mas são independentes eu gostaria de fazer

a -> ( maquina x faz b; maquina y faz c) -> d

na melhor das hipoteses eu tenho o tempo total como sendo

t_a + max(t_b,t_c) + t_d

K

Olá Tiago, desculpe-me a demora na resposta, mas estava esperando o pessoal postar suas soluções :slight_smile: .

Bom, na minha ótica BPEL seria o melhor candidato pra resolver seu problema, em que você tivesse que quebrar a cabeça com nada. Poderia manter a implementação do seu projeto em Ruby e “Orquestrar” as chamadas e recursos diramente da engine.

Algumas implementações como o produto da Oracle, possuem até adaptadores à arquivos (FileAdapter), Queue e DB, então ficaria bem suave.

Dê uma olhada no exemplo a seguir de como seria uma solução desse tipo - http://blogs.sun.com/malkit/entry/dynamic_services_composition_in_bpel

Parece complexo, mas um profissional com um pouco de experiência monta uma solução dessa em uma tarde.

Aliás, daria pra você usar uma Engine dessas e usar uma implementação de WS-Ruby como da WSo2 que suporta o que você precisaria:

  • SOAP MTOM
  • WS-Addressing
  • WS-Security
  • WS-SecurityPolicy
  • WS-ReliableMessaging

http://wso2.org/project/wsf/ruby/1.1.0/docs/index.html e faria somente a coordenação no BPEL.

Voltando ao mundo Ruby, o buraco vai ser mais embaixo, exatamente por que coisas que a especificação WS* cuida pra você, como WS-Coordination, você terá que fará na mão, montando um monitor, compensação, para casos de falha e por aí vai.

Distribuir o processamento, vai exigir um framework pra vc montar suas queues, diria que pode usar o Starling, ActiveMessaging ou até mesmo JMS se tiver usando JRuby em cima de um ApplicationServer - dica, use o framework do Leandro Silva (codezone) http://github.com/leandrosilva/jsparrow

Uma das maneiras de garantir a atomicidade é utilizar um cache e também de ter um pouco mais de segurança no seu processamento, já que este pode ser distribuído, então utilize algo como Redis.

De qualquer maneira terá que construir um monitor e cobrir as falhas, compensação dependendo da sua regra de negócio.

Aqui tem um projetinho, Resque do pessoal do Redis para você se inspirar : http://github.com/defunkt/resque

Por fim, a última abordagem que vem à minha cabeça é utilizar um STM Framework (http://en.wikipedia.org/wiki/Software_Transactional_Memory) para garantir a atomicidade da operação. Essa idéia é originária do Clojure, mas descobri isso num framework Scala chamado Akka - http://akkasource.org.

Aliás seria bacana se tivesse algo assim pro mundo Ruby - http://doc.akkasource.org/stm , mas não achei nada concreto, o mais perto foi esse blog: http://moonbase.rydia.net/mental/blog/programming/ruby-stm-first-round.html

PS: Acredito que você entende por mais simples, pode nesse caso consumir um bom tempo de desenvolvimento e “quebra” cabeça :slight_smile:

Depois poste sua solução, estou curioso pra saber como resolveu :smiley:

M

peczenyj:
ok, tenho uma exigencia de SLA de alguns minutos

ao inves de fazer

a -> b -> c -> d

onde b e c demoram muito mas são independentes eu gostaria de fazer

a -> ( maquina x faz b; maquina y faz c) -> d

na melhor das hipoteses eu tenho o tempo total como sendo

t_a + max(t_b,t_c) + t_d

Sei que Ruby não tem threads, mas se vc arrumasse uma maneira de disparar b e c para rodar como processos na mesma máquina “simultaneamente”, ao inves de sequencialmente, não conseguiria atender as exigencias do SLA?

A

Processamento batch?

Quartz. O Spring tem um módulo para ajudar na implementação com Quartz.

K

andre_salvati:
Processamento batch?

Quartz. O Spring tem um módulo para ajudar na implementação com Quartz.

Olá André, se for batch com Spring, usa logo o SpringBatch :smiley: http://static.springsource.org/spring-batch/

L

Olá

Não sei se entendi bem. Mas me veio a cabeça a idéia de você abandonar completamente o file system e armazenar as coisa em um CouchDb eventualmente replicado.

Não consegui achar nenhuma outra idéia mantendo o uso do file system (que realmente é bem simples [*])

[*] O uso de file system para trocar informações entre sistemas heterogêneos é o que sempre usaram os programadores mais ágeis que nunca quiseram se meter com idéias toscas como CORBA, RPC e outras coisas desnecessariamente complicadas e às vezes dependentes de intermediação paga.

[]s
Luca

K

Olá Luca, exatamente minha sugestão só que usando o Redis.

Vi uma excelente apresentação no GuruSP (Felipe Vieira) sobre o case do Boo-box e chegaram usar CouchDB. O ganho que tiveram com o Redis foi enorme, acredito ser um produto mais maduro. Melhor otimização de memória (muito mesmo) e performance em menos de 3% do servidor, contra uns 50% do CouchDB.

Outra coisa que o Redis tem um mencanismo de persistência em filesystem, caso ele precise.(ainda em beta) -http://code.google.com/p/redis/wiki/Features

L

Olá

Pena, perdi esta. Tinha entendido sua sugestão como se fosse apenas para cache.

Adoro soluções simples como esta de usar o file system. Já usei muito para integrar VB com Java, Delphi com Java, C com Java e até mesmo C com C em uma aplicação dividida em 4 módulos diferentes.

Uma idéia que me ocorreu agora mas que pode ser puro devaneio é o uso de SkipLists para controlar as concurrent priority queues. Para isto eu usaria Java e JRuby. Pode ser que esteja viajando mas que ficaria bunitinho não tenho dúvidas.

[]s
Luca

K

Luca:
Olá

Não sei se entendi bem. Mas me veio a cabeça a idéia de você abandonar completamente o file system e armazenar as coisa em um CouchDb eventualmente replicado.

Não consegui achar nenhuma outra idéia mantendo o uso do file system (que realmente é bem simples [*])

[*] O uso de file system para trocar informações entre sistemas heterogêneos é o que sempre usaram os programadores mais ágeis que nunca quiseram se meter com idéias toscas como CORBA, RPC e outras coisas desnecessariamente complicadas e às vezes dependentes de intermediação paga.

[]s
Luca

Luca só um detalhe da sua afirmação quanto à CORBA e RPC, em muitos cenários havia a necessidade da operação ser online e na época ou era isso ou solução proprietária como Tuxedo.

L

Olá

Kenobi:
... só um detalhe da sua afirmação quanto à CORBA e RPC, em muitos cenários havia a necessidade da operação ser online e na época ou era isso ou solução proprietária como Tuxedo.

A necessidade de ser on line não justifica o acoplamento (a palavra mais adequada é engrunvinhamento porque é mais feia e assim descreve melhor como ficam os sistemas).

Cansei de usar troca de arquivos para comunicar sistemas de cartão de crédito que precisam responder ao cliente lá na ponta em poucos segundos. Como era simples, funcionava rápido. Quando por outras exigências trocamos isto em um sistema para usar sockets, levamos um bom tempo apanhando dos erros de rede até que os dois sistemas conseguissem falar o mesmo ISO 8583.

Nunca vi nada que justificasse o uso de CORBA, DCOM e outros complicômetros. Foi um modismo idiota de um tempo em que os "especialistas", isto é, vendedores de ORBs, tentavam convencer o mundo que integração precisava de RPC. Para usar estas porcarias ambos os sistemas precisavam de modificações grandes e complexas.

Só para depurar estes monstrengos era preciso de gente muito qualificada.

Veja abaixo como ficaria simples e desacoplado (tirado do livro Camel in Action usando Apache Camel) para fazer polling em um diretório [color=darkblue]inbox[/color] checando de tempos em tempos se chegou alguma coisa e em caso afirmativo rotear para um diretório [color=darkblue]outbox[/color]. Isto é o mais básico exemplo de troca de mensagem que consigo imaginar. Mas não está muito diferente do que era feito nos tempos em que trabalhei com cartão de crédito.

public class FileCopierWithCamel { 
   public static void main(String args[]) throws Exception {

      CamelContext context = new DefaultCamelContext(); 
      context.addRoutes(new RouteBuilder() {
         public void configure() { 
            from("file:data/inbox?noop=true")	
               .to("file:data/outbox");	
         }
      });
      context.start(); 
      Thread.sleep(10000); 
      context.stop();
   }
}

[]s
Luca

P

O problema não são threads, são 3 processamentos (atualmente) que são muito pesados e ocupam todas os cores de cpu possiveis, 2 deles são encoding de video usando ffmpeg e 1 deles usa ImageMagick (que comparado com o ffmpeg é até leve).

Estou inclinado a resolver da seguinte forma

Quando precisar fazer:
Inicio -> [B,C,D] -> Fim

Eu faço

Inicio -> X - > Fim

onde X é um processo que orquestra 3 processos em servidores distribuidos, dedicados a este processamento.

Nesse caso eu separo o refactoring em 2 partes

  1. Distribuo apenas o processamento “especial” e atendo o meu problema, reduzindo o escopo.
  2. Transformo o meu sistema em outra coisa, talvez até com outra tecnologia (Scala ??)

Se eu reduzir escopo ao que eu realmente quero, que é rodar 3 processos em paralelo em 3 maquinas diferentes, tenho espaço para experimentar tecnologias (como couchDB distribuido, BPEL) e mantenho um plano B que seria voltar ao processo sincrono em uma emergência.

Como o processo que trabalha com encoding de video é o mais pesado, vou começar por eles, assim que reler as sugestões do Kenobi & cia :slight_smile:

L

Olá

Se o número de processos é conhecido diante mão e são somente 3, então nem precisa de orquestração, BPEL, CouchDB, etc. Basta ficar fazendo polling nos diretórios que os processos B, C e D gravam os resultados dos seus processamentos.

[]s
Luca

M

O problema não são threads, são 3 processamentos (atualmente) que são muito pesados e ocupam todas os cores de cpu possiveis, 2 deles são encoding de video usando ffmpeg e 1 deles usa ImageMagick (que comparado com o ffmpeg é até leve).

Basta colocar o processamento pesado por detras de uma interface remota. Acredito que não ha necessidade de trocar a arquitetura de pipes & filters por uma arquitetura distribuida só por causa disso.

K
Luca:
Olá
Kenobi:
... só um detalhe da sua afirmação quanto à CORBA e RPC, em muitos cenários havia a necessidade da operação ser online e na época ou era isso ou solução proprietária como Tuxedo.

A necessidade de ser on line não justifica o acoplamento (a palavra mais adequada é engrunvinhamento porque é mais feia e assim descreve melhor como ficam os sistemas).

Cansei de usar troca de arquivos para comunicar sistemas de cartão de crédito que precisam responder ao cliente lá na ponta em poucos segundos. Como era simples, funcionava rápido. Quando por outras exigências trocamos isto em um sistema para usar sockets, levamos um bom tempo apanhando dos erros de rede até que os dois sistemas conseguissem falar o mesmo ISO 8583.

Nunca vi nada que justificasse o uso de CORBA, DCOM e outros complicômetros. Foi um modismo idiota de um tempo em que os "especialistas", isto é, vendedores de ORBs, tentavam convencer o mundo que integração precisava de RPC. Para usar estas porcarias ambos os sistemas precisavam de modificações grandes e complexas.

Só para depurar estes monstrengos era preciso de gente muito qualificada.

Veja abaixo como ficaria simples e desacoplado (tirado do livro Camel in Action usando Apache Camel) para fazer polling em um diretório [color=darkblue]inbox[/color] checando de tempos em tempos se chegou alguma coisa e em caso afirmativo rotear para um diretório [color=darkblue]outbox[/color]. Isto é o mais básico exemplo de troca de mensagem que consigo imaginar. Mas não está muito diferente do que era feito nos tempos em que trabalhei com cartão de crédito.

public class FileCopierWithCamel { 
   public static void main(String args[]) throws Exception {

      CamelContext context = new DefaultCamelContext(); 
      context.addRoutes(new RouteBuilder() {
         public void configure() { 
            from("file:data/inbox?noop=true")	
               .to("file:data/outbox");	
         }
      });
      context.start(); 
      Thread.sleep(10000); 
      context.stop();
   }
}

[]s
Luca

Ahhhh Luca, mas se eu falasse em ESB aqui alguém me dava um tiro !! :-)

Tá aí e outra Apache Camel tem DSL pra Scala - http://camel.apache.org/scala-dsl-eip.html

Ae Tiago o projeto vai ficar very nice !! :)

A

Realmente, acho que o camel é uma ótima pedida!

Criado 11 de abril de 2010
Ultima resposta 14 de abr. de 2010
Respostas 18
Participantes 7