segunda-feira, 27 de abril de 2009

JMS: um pouco sobre alguma coisa a respeito!

Boas!

Onde trabalho, uma vez por semana algum funcionário/consultor/estagiário sempre (ou quase sempre) apresenta uma palestra sobre algum assunto de interesse técnico. Um esquema para diluir o conhecimentos entre os profissionais de TI. Um salve para o criador!

Enfim, a algumas semanas tive o prazer de falar sobre TDD, e a alguns dias foi minha vez de falar sobre JMS, mesmo sem quase domínio algum do assunto.

O bom dessas palestras é que você acaba sendo um pouco forçado a se aprofundar no tema, e, bem, literalmente me enfiei na fila. Como não tenho paciência para fazer apresentações bem acabadas como o Casca e o Sakurai, então vai no "teclador" mesmo um resumão sobre JMS, o assunto mais fresco na memória.

JMS, ou Java Message Service, é uma API para troca de mensagens entre aplicações baseadas na J2EE. JMS serve basicamente para largar alguma coisa lá rodando e ir fazer outra coisa. Algo como deixar aquele download de 700MB rolando na sua conexão de 128k: você larga e vai assistir TV.

Em tudo que é artigo sobre JMS vão falar sobre point-to-point e publish-subscribe, os tais paradigmas. No primeiro, a mensagem é enviada apenas para UM consumidor, através de uma fila (Queue). No segundo, um tópico (Topic) é publicado e vários assinantes -subscribers- podem consumir a mensagem. Em ambos os casos, quem gera a mensagem é chamado de produtor (Producer). Quem recebe é chamado de consumidor (Consumer).

Mas espera um pouco, o que é a tal da "mensagem"? Tecnicamente, qualquer sequencia de bytes serializável. Um texto, um objeto qualquer, um stream de primitivos, bytes a esmo.

Outro termo que aparece o tempo todo é MOM - Message-Oriented Middleware. O MOM é o serviço central de mensagens, na prática, o software que você está usando para administração dos canais. Existem MOMs para todos os gostos, JBoss MQ, IBm MQSeries, BEA Weblogic, and so on.


Exemplo de código de um Queue Producer. Os códigos estão mal formatados porque não sei ainda como formata código decentemente aqui, mas vá lá.

...
InitialContext ctx = new InitialContext(...);

Queue queue = (Queue) ctx.lookup("queue/MyQueue");

QueueConnectionFactory factory =
(QueueConnectionFactory) ctx.lookup("ConnectionFactory");


QueueConnection cnn = factory.createQueueConnection();

QueueSession sess =
cnn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);


/* Objeto que implementa Message: BytesMessage, MapMessage, ObjectMessage, StreamMessage, TextMessage */
TextMessage msg = sess.createTextMessage("Mensagem 1!!");

QueueSender sender = sess.createSender(queue); sender.send(msg);
...



Exemplo de código de Topic Producer :

...
InitialContext ctx = new InitialContext(...);

Topic topic = (Topic) ctx.lookup("topic/MyTopic1");

TopicConnectionFactory factory =
(TopicConnectionFactory) ctx.lookup("ConnectionFactory");


TopicConnection cnn = factory.createTopicConnection();

TopicSession sess =
cnn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);

TextMessage msg = sess.createTextMessage("Uma mensagem");

TopicPublisher sender = sess.createPublisher(topic);

sender.send(msg);
...


A estrutura de ambos seguem um mesmo padrão: localizar o destino (fila/tópico), localizar uma fábrica de conexões, criar uma conexão, criar uma sessão e, com a sessão e o destino "em mãos", enviar a mensagem! Como é um padrão, você pode criar algum método utilitário (algumas pessoas acham pecado usar o termo "método utilitário", mas é isso aí mesmo), numa classe tipo ServiceAlgumaCoisaLocator.


Para falar de Consumer, vamos ver um pouco sobre MDB (Message-Driven Beans). Um MDB é um bean que permite processar mensagens de forma assíncrona, e funciona como um message listener (implementa javax.jms.MessageListener).

Os clientes não acessam MDBs diretamente, e se parece muito com um Session bean: não pode conter estado, as instâncias são equivalentes, e o contêiner pode gerenciar um pool de MDBs.

Trecho de código exemplo de MDB (EJB 3.0):

@MessageDriven(
activationConfig =
{ @ActivationConfigProperty(
propertyName = "destinationType",

propertyValue = "javax.jms.Queue"),

@ActivationConfigProperty(
propertyName = "destination",

propertyValue= "queue/MyQueue")

})


public class TesteMessage1 implements MessageListener {
public TesteMessage1() { }

public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
System.out.println(((TextMessage)message).getText());
}

}
catch (JMSException e) { ... }
}
}


Em relação ao JMS, algumas vantagens:

- Escalabilidade: na teoria (como tudo), é só aumentar os servidores, não precisa mexer nos componentes;
- Comunicação assincrona: componentes podem fazer outras tarefas enquanto largam algum trabalho na fila;
- Desacoplamento: todas as partes trabalham com a mesma interface; não é preocupação dos componentes a qualidade do serviço e sim do MOM;
- Flexibilidade: tudo que circula é mensagem, se os componentes entendem, o resto não importa.


E algumas desvantagens:

- Camadas adicionais para repasse de mensagens;
- Centralização pode levar a falhas no sistema todo!
- O consumidor precisa do instanceof pra identificar o tipo da mensagem. Éca! :)


Isso aí, foi só uma pequena pincelada sobre JMS! Quando estiver com paciência, escrevo sobre a palestra de TDD, que é um assunto bala também!

Um agradecimento pra todos que me deram dicas sobre o assunto!

Até!

2 comentários:

Casca disse...

A palestra foi muito boa para tirar o mito que utilizar filas é algo difícil e complicado.

Flw!!!

OBS: li seu comentário, um dia atualizarei meu blog! rss

Felipe Zanardo Affonso disse...

Eu sou um dos que apresentou, mas a palestra não foi tão bem "acabada" como a do Casca e do Sakurai.
Sua palestra foi dahora! Aprendi tanto que ainda fiz um comentário na palestra de Flex!

É isso aí André!