Desenvolvimento em Java 2.0: Sistema de mensagens baseado em nuvem com o Amazon SQS

Uso pré-pago com o sistema de enfileiramento de mensagens da Amazon

O Simple Queue Service (SQS) da Amazon pega o que precisa do message-oriented middleware (MOM), mas não prende você a nenhuma linguagem de implementação ou estrutura. Aprenda como usar o Amazon SQS para aliviar o fardo de instalar e manter um sistema de enfileiramento de mensagens ao mesmo tempo que aproveita a escalabilidade de modelo pré-pago do AWS.

Andrew Glover, Author and developer, Beacon50

Andrew Glover é desenvolvedor, autor, palestrante e empreendedor com uma paixão por desenvolvimento direcionado por comportamento, integração contínua e desenvolvimento de software Agile. Ele é o fundador da easyb , estrutura Behavior-Driven Development (BDD) e é o coautor de três livros: Continuous Integration, Groovy in Action e Java Testing Patterns. Você pode acompanhá-lo em seu blog e seguindo-o no Twitter.



19/Abr/2011

Sobre esta série

O cenário de desenvolvimento em Java mudou radicalmente desde que a tecnologia Java™ surgiu pela primeira vez. Graças a estruturas maduras de software livre e infraestruturas de implementação confiáveis de aluguel, agora é possível montar, testar, executar e manter aplicativos Java de forma rápida e barata. Nesta série, Andrew Glover explora o espectro de tecnologias e ferramentas que tornam este novo paradigma de desenvolvimento Java possível.

Filas de sistemas de mensagens são comuns em uma variedade de arquiteturas de software e domínios, incluindo sistemas financeiros, de saúde e o segmento de viagens. No entanto, o message-oriented middleware (MOM) — o paradigma dominante de sistemas de mensagens para sistemas distribuídos — requer que um sistema de enfileiramento seja especialmente instalado e mantido. Neste mês, eu apresento uma alternativa baseada em nuvem para esse sistema de mensagens de trabalho tão intenso: o Simple Queue Service (SQS) da Amazon.

Assim como frequentemente faz sentido hospedar um aplicativo da Web no Google App Engine ou Amazon Elastic Beanstalk (consulte Recursos), também faz sentido aproveitar um sistema de mensagens em nuvem. De qualquer forma, você é capaz de dedicar mais tempo à codificação do aplicativo em vez da instalação e manutenção da sua infraestrutura subjacente.

Neste artigo, você aprenderá como o Amazon SQS alivia o fardo de instalar e manter um sistema de enfileiramento de mensagens. Também terá a oportunidade de praticar a criação de filas de mensagens do SQS, soltando e recuperando mensagens nelas. Finalmente, mostrarei o que acontece quando incluo um sistema de mensagens no Magnus, o aplicativo da Web remoto que usei no mês passado naIntrodução ao Amazon Elastic Beanstalk.

Alô, quem fala? É o MOM.

Message-oriented middleware, ou MOM, é um termo que descreve sistemas fracamente acoplados que se comunicam através de filas de mensagens. Ao invés de serem componentes de um sistema fortemente acoplado (através de dependências de tempo de compilação, por exemplo), eles são distribuídos por uma rede. Esse efeito distribuído, no qual filas de mensagens são o meio para a comunicação, permite que os sistemas de mensagens sejam escalados.

Tradicionalmente, os arquitetos decidem quais componentes se comunicam uns com os outros em um sistema orientado a mensagens. Apesar de toda comunicação acontecer através da passagem de mensagens, a mensagem em si se encontra frequentemente em um formato genérico multiplataforma. As mensagens podem ser simples cadeias de caractere ou até documentos codificados usando XML ou JSON.

Como uma arquitetura MOM desacopla componentes e permite uma comunicação multiplataforma entre eles, as unidades individuais podem ser heterogêneas. Ou seja, os componentes em uma arquitetura distribuída podem ser escritos em linguagens diferentes, como linguagem Java C# e Ruby. Os componentes também podem existir em plataformas diferentes, como UNIX® e Windows®. Além disso, MOMs tornam mais fácil a integração de sistemas. Como middleware, MOMs podem se conectar com sistemas legados e também com os mais novos. Isso ocorre porque a API entre os componentes é simplesmente uma mensagem, que pode ser qualquer coisa desde um documento XML, passando por um objeto serializado, até uma simples cadeia de caractere.

GAE é sua MOM!

As filas de mensagens em um sistema MOM são as tubulações da Web: elas conectam vários componentes de sistema para permitir que as mensagens fluam livremente entre eles. Ao que se revela, o GAE é um excelente exemplo de sistema de middleware orientado a mensagens.

Como qualquer bom MOM, o Google App Engine usa filas de mensagens para desacoplar processos de sistemas. Especificamente, as filas do GAE tornam possível descarregar processos de longa execução das solicitações da Web. Usando o GAE, você solta URLs que apontam para servlets ou JSPs em filas de mensagens, que são então recebidas e processadas pelos serviços do GAE. Servlets são chamados assincronicamente em relação à sequência lógica principal de um aplicativo da Web. (Consulte Recursos para saber mais sobre GAE).

Porém, enfileirar processos de execução mais longa para gerenciar a duração dos processos principais não é algo exclusivo do GAE. Esse recurso similar ao MOM é oferecido com outras implementações PaaS como Heroku. Com o Amazon SQS, porém, é possível fazer isso com facilidade em qualquer aplicativo da Web, não importa a plataforma.


Introdução ao Amazon SQS

O Amazon SQS oferece diversos recursos que devem ser familiares se você já usou filas de mensagens no JMS.

Amazon SQS não é JMS

Filas de mensagens na plataforma Java não são novidade, como exemplificado pela especificação JMS. O JMS já tem mais de uma década de vida e inclui uma lista impressionante de implementações, incluindo RabbitMQ, ActiveMQ da Apache e até mesmo o WebSphere da IBM.® MQ. Porém, a API do Amazon SQS não implementa nenhuma interface JMS. Na verdade, pode-se até dizer que ela é mais simples que a JMS e que é muito fácil de instalar e executar.

Amazon SQS:

  • Permite que múltiplos processos leiam e gravem a partir da mesma fila. Também trava as mensagens durante o processamento, o que garante que a mensagem só será processada por um leitor, mesmo que múltiplos processos estejam lendo uma única fila.
  • Tira proveito da arquitetura massivamente redundante da Amazon para oferecer níveis extremamente altos de disponibilidade em face de acessos simultâneos. Isso também garante a entrega de uma mensagem (pelo menos uma vez).
  • Requer pagamento apenas pelo o que você usou. Para o Amazon SQS, isso significa que você paga $0,000001 por mensagem. AWS oferece atualmente um nível de uso grátis, no qual as primeiras 100.000 mensagens por mês são grátis. Lembre-se de que há tarifas de largura de banda cobradas por gigabyte, o que é comum a todos os produtos AWS.

A introdução ao SQS é tão simples como todas as outras coisas no AWS. Se ainda não tiver uma conta AWS, primeiro crie uma. Em seguida, ative o Amazon SQS. Finalmente, use a interface AWS Java SDK para publicar e ler mensagens baseadas em nuvem! Mais sobre como as escrever abaixo).


Escrevendo mensagens SQS

Em conformidade com o nome do Amazon SQS, a lógica por trás da leitura e escrita em uma fila é a simplicidade em pessoa. Em primeiro lugar, estabeleça uma conexão com o AWS usando um segredo e uma chave de acesso válida, como mostrado na Listagem 1:

Lista 1. Estabelecendo uma conexão com o AWS
AmazonSQS sqs = new AmazonSQSClient(new BasicAWSCredentials(AWS_KEY, AWS_SECRET));

Em seguida, você precisa de uma fila. Na API AWS, a chamada para createQueue, mostrada na Listagem 2, não cria necessariamente uma nova fila toda vez. Se a fila já existe, seu identificador é retornado. No SQS, as filas são apenas URLs; consequentemente, um identificador de fila também é apenas uma URL. Observe que na API AWS SDK, a URL de Fila é um tipo de Cadeia de caractere e não a URL Java.

Lista 2. Obtendo um identificador para uma Fila
String url = sqs.createQueue(new CreateQueueRequest("a_queue")).getQueueUrl();

Uma vez que tenha uma fila, você pode escrever uma mensagem nela. O formato de mensagem do SQS é similar ao do SimpleDB (consulte Recursos), no sentido de que as mensagens são Cadeia de caracteres. Lembre-se, no entanto, de que uma cadeia de caractere pode ser facilmente estruturada, e assim facilmente analisada, ao tornar seu formato JSON ou XML válido.

Lista 3. Enviando mensagens via SQS
sqs.sendMessage(new SendMessageRequest(url, "It's a wonderful life!"));

SQS mantém as coisas simples

Lembre-se de que o Amazon SQS é, em primeiro lugar, simples, o que significa que ele não possui os recursos extras com os quais você pode estar acostumado. Por exemplo, o SQS não realiza notificações proativas, portanto os leitores de uma fila SQS devem pesquisar periodicamente para ver se ela contém novas mensagens. Embora não seja terrível, isso adiciona gastos adicionais ao aplicativo, o que pode não ser aceitável em alguns casos. O Amazon Simple Notification Service (SNS) resolve esse problema, mas isso é assunto para outro artigo.

O tamanho das mensagens é limitado. Por padrão, uma mensagem não pode ser maior que 8kb. Se precisar usar mensagens maiores, é sempre possível dividi-las, identificando as partes individuais com IDs de sequência. As mensagens podem ser então remontadas no lado receptor.

É isso — foi preciso apenas três linhas de código para colocar uma mensagem em uma fila SQS.

Sobre o SDK AWS

Você deve estar notando um padrão familiar no SDK AWS, principalmente se já leu minha introdução ao SimpleDB (consulte Recursos). Como tudo no AWS é um serviço da Web, toda a comunicação acontece por HTTP. Consequentemente, a API imita solicitações lógicas através de objetos parecidos com Request, como SendMessageRequest ou CreateQueueRequest. Em ambos os casos, os nomes descrevem a intenção do objeto.

Outra coisa a se notar é que as mensagens colocadas no SQS são duráveis: elas ficam lá até que você as remova. (As mensagens acabam desaparecendo com o tempo se você não as remover; o valor padrão para a expiração automática é quatro dias). O Amazon SQS emprega uma simples estratégia de travamento quando as mensagens são buscadas para leitura — para um evento de leitura, a mensagem não estará disponível para outros processos de leitura simultâneos por um período de tempo, conhecido como tempo limite de visibilidade da mensagem. Esse valor é definido como 30 segundos por padrão, apesar de ser possível alterar a duração como bem entender.

A durabilidade das mensagens na infraestrutura da Amazon é tranquilizadora. Assim como o SimpleDB, ou mesmo o S3, os componentes no mundo AWS são enormemente redundantes. Se o processo (ou processos) do seu leitor for encerrado inesperadamente durante o processamento de mensagens, há uma grande chance da mensagem ainda estar por lá. Além disso, se algum ativo na rede AWS também acabar se estragando, podemos apostar que as mensagens críticas não serão perdidas — elas ainda existirão em muitas outras máquinas. Finalmente, como é o caso com todos os outros produtos AWS, é possível definir o local físico da sua infraestrutura de mensagens por região: EUA, EU e assim por diante.


Lendo mensagens SQS

Escrever uma mensagem para uma fila SQS exige três linhas de código. Ler uma mensagem exige apenas algumas mais. Na verdade, as duas primeiras linhas são idênticas, já que são necessários uma conexão ao AWS e um identificador para a mesma fila. O Amazon SQS não oferece qualquer funcionalidade de retorno de chamada ou de notificação proativa da chegada de mensagens. É necessário pesquisar uma fila SQS periodicamente para ver se ela tem algo para entregar. Consequentemente, ler uma fila SQS requer aquelas poucas linhas adicionais de código.

Há um pequeno porém na implementação da estratégia de pesquisa: é necessário fazer uma verificação para assegurar que você realmente recebeu uma mensagem válida antes de tentar processá-la. Se não fizer isso, com certeza acabará vendo o nefário NullPointerException.

Por exemplo, caso eu tenha uma conexão válida com o AWS e um identificador de uma fila contendo mensagens, eu posso recuperar as mensagens como mostrado na Listagem 4:

Lista 4. Recebendo mensagens via SQS
 while (true) {
  List<Message> msgs = sqs.receiveMessage(
     new ReceiveMessageRequest(url).withMaxNumberOfMessages(1)).getMessages();

  if (msgs.size() > 0) {
   Message message = msgs.get(0);
   System.out.println("The message is " + message.getBody());
   sqs.deleteMessage(new DeleteMessageRequest(url, message.getReceiptHandle()));
  } else {
    System.out.println("nothing found, trying again in 30 seconds");
    Thread.sleep(3000); 
  }
}

Na Listagem 4, a referência ao sqs é um tipo do AmazonSQS como visto na Listagem 1. Esse objeto fornece um método receiveMessage que aceita um ReceiveMessageRequest. ReceiveMessageRequests podem ser configurados para solicitar um número definido de mensagens em uma fila. No meu caso, eu o configurei simplesmente para pegar uma mensagem por vez. Não importa o número de mensagens que eu solicitar, o método receiveMessage retorna uma Lista de tipos de Message .

Implementando uma estratégia de pesquisa

Como mencionei anteriormente, a leitura SQS é feita com pesquisas; além disso, o método receiveMessage não é bloqueador. Consequentemente, eu preciso verificar se a Lista (msgs) correspondente contém alguma coisa. Se nada foi recuperado de uma fila, a chamada para getMessages no ReceiveMessageRequest retornará uma Lista vazia, ao invés de null.

Presumindo que tenha recuperado uma mensagem válida, eu posso obter sua carga útil ou corpo através da chamada getBody . Lembre-se de que uma vez que você tenha o identificador de uma mensagem válida, o SQS a trava. Por padrão, temos 30 segundos para fazer algo com a mensagem. Devemos excluir a mensagem se desejamos removê-la permanentemente do processamento. Portanto, emitimos uma chamada deleteMessage , que recebe uma DeleteMessageRequest.

Uma instância Message é distinguida por seu identificador de recebimento, como ID. O identificador não tem relação direta com a mensagem, mas mais com o evento de que está sendo lida. Uma mensagem que foi lida mais de uma vez (como se não tivesse sido excluída, ou se um processo de leitura falhou) poderia ter vários e diferentes identificadores de recebimento. Como resultado, quando deseja excluir uma mensagem, é necessário primeiro fornecer seu identificador de recebimento através da chamada getReceiptHandle .

Ao invés de verificar continuamente para ver se minha fila tem uma mensagem, eu forneço uma função de hibernação que espera 30 segundos caso nenhuma mensagem tenha sido recuperada. Obviamente em alguns casos, a hibernação pode não ser uma boa ideia, ou uma pausa mais longa pode se fazer necessária.

Com essas poucas linhas de código, eu praticamente abordei todo o Amazon SQS. Apesar de o SDK AWS fornecer várias outras funções e recursos, o código até aqui é o bastante para ler e escrever mensagens para filas do SQS.

Agora vejamos o que acontece quando o colocamos em uso.


Magnus encontra o Amazon SQS

Mês passado, eu criei um simples aplicativo da Web remoto chamado Magnus, que eu usei para demonstrar alguns dos recursos do Amazon Elastic Beanstalk (consulte Recursos). O Magnus tem a capacidade bem interessante de armazenar informações de local recebidas dos dispositivos remotos dos donos das contas — exatamente o tipo de informação que muitas pessoas querem fornecer, e que muitas outras querem consumir.

Capturar o local onde se encontra uma pessoa é tudo muito legal, mas o que as pessoas realmente gostam de ver são gráficos (e botões brilhantes com cantos arredondados). Gráficos e analíticas podem ser caros do ponto de vista do processamento quando há toneladas de dados para processar. (Alguém se lembra do Hadoop?) A técnica testada e comprovada de extrair, transformar e carregar, ou ETL, é uma maneira de lidar com isso. ETL é um termo bastante amplo que abrange muitas coisas. (As pessoas constroem carreiras e as empresas negócios acerca desse acrônimo!) Neste caso, o ETL simplesmente implica que vou analisar alguns dados de MongoDB e criar novos documentos com base nesses dados.

ETL com Amazon SQS

No que diz respeito à analítica de dados, há inúmeras possibilidades para o que podemos pedir dos dados, e para as respostas que podemos oferecer. O aplicativo da Web Magnus utiliza uma fatia bem pequena desse potencial: ele pega e apresenta dados relacionados a coordenadas geográficas, horários e contas do usuário.Tecnicamente, o Magnus tem interesse na latitude e longitude do local, ID da conta do usuário, registros de data e hora e as relações entre esses dados específicos.

O Magnus poderia fornecer uma representação gráfica desses dados mostrando as contas do usuário por área geográfica (talvez um mapa com marcadores localizando um dono de conta em um momento específico). Ou poderia mostrar como um dono de conta/usuário deslocou-se em uma determinada região (outro mapa). Fornecer esse tipo de informação envolve um processo parecido ao ERL que ocorre off-line.Fornecer os dados em tempo real, à medida que são gerados, pode ser muito caro do ponto de vista do processamento. Então conceba essas analíticas como quase em tempo real.

Para usar o Amazon SQS no Magnus, eu preciso fazer algumas configurações preliminares. Em primeiro lugar, preciso de uma maneira de obter credenciais AWS. Particularmente eu gosto do Play (consulte Recursos), portanto irei usá-lo como minha estrutura de desenvolvimento de aplicativo. Para obter as credenciais, eu posso usar o arquivo application.conf do Play, um arquivo de propriedades que é lido automaticamente.

Lista 5. Incluindo dados de configuração do AWS no application.conf do Play
#AWS configuration
aws_access_key_id=1S..........MR2
aws_secret_access_key=S3.........ZM

Uma vez que as propriedades tenham sido definidas, eu posso obtê-las facilmente através de uma chamada para o objeto Play do Play, como mostrado na Listagem 6:

Lista 6. Obtendo informações do AWS no Play
public class Application extends Controller {

 private static final String AWS_KEY = 
    Play.configuration.get("aws_access_key_id").toString();
 private static final String AWS_SECRET = 
    Play.configuration.get("aws_secret_access_key").toString();

//....
}

Com o encanamento definido, podemos começar a trabalhar. O código na Listagem 7 é similar ao fragmento que usei mês passado na minha introdução ao Amazon Elastic Beanstalk. Neste caso, simplesmente atualizei o saveLocation com um pouco de código para colocar um documento JSON simples em uma fila chamada "locations_queue". O JSON basicamente se parece com isto: {"id":"4d6baeb52a54f1000001"}. O ID do local salvo é fornecido para que o destinatário da mensagem consulte e analise.

Lista 7. Um método saveLocation para colocar mensagens no SQS
public static void saveLocation(String id, JsonObject body) throws Exception {
 String eventname = body.getAsJsonPrimitive("name").getAsString();
 double latitude = body.getAsJsonPrimitive("latitude").getAsDouble();
 double longitude = body.getAsJsonPrimitive("longitude").getAsDouble();
 String when = body.getAsJsonPrimitive("timestamp").getAsString();

 SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm");
 Date dt = formatter.parse(when);

 ObjectId oid = new Location(id, dt, latitude, longitude).save();

 AmazonSQS sqs = new AmazonSQSClient(new BasicAWSCredentials(AWS_KEY, AWS_SECRET));

 Map mp = new HashMap<String, String>();
 mp.put("id", oid.toString());

 String url = sqs.createQueue(new CreateQueueRequest("locations_queue")).getQueueUrl();
 sqs.sendMessage(new SendMessageRequest(url, new Gson().toJson(mp)));

 renderJSON(getSuccessMessage());
}

Um encontro com o Ruby?

Agora que as mensagens estão sendo colocadas em uma fila SQS, eu preciso tirá-las da fila e fazer um pouco de processamento. Se você se lembra bem, uma das vantagens de um MOM é que ele permite arquiteturas heterogêneas. Para esse fim, o lado do leitor SQS poderia ser escrito em uma linguagem que não fosse Java, e poderia até mesmo ser executado em outra plataforma!

Como eu posso, basicamente, fazer o meu processamento de analítica onde preferir, eu vou fazer isso no Ruby — para ficar bem na foto com os caras legais.

Na Listagem 8, pedi ajuda da gem right_aws do Ruby para me ajudar a trabalhar com o SQS. De várias formas, pode-se conceber umagem como um arquivo jar. A biblioteca right_aws é muito parecida com o SDK para Java da Amazon, mas menos prolixa e mais direta para se trabalhar.

Lista 8. Criando uma conexão e fila no Ruby para SQS
require "right_aws"
#...
sqs  = RightAws::SqsGen2.new(aws_access_key_id, aws_secret_access_key)
queue = sqs.queue('locations_queue')

Como se pode ver, as duas linhas de código relevante da Listagem 8 estabelecem uma conexão com o AWS e pegam um identificador para minha fila chamada 'locations_queue'.

A seguir, eu estabeleço o mecanismo de pesquisa, como mostrado na Listagem 9. A referência a @queue é a mesma variável queue da Listagem 8. Neste caso, porém, ela foi definida como parte de uma classe. Portanto, na Listagem 9, estou fazendo referência direta a uma variável de instância com a sintaxe@ do Ruby.

Lista 9. Processando mensagens do SQS
def process_messages()
  while true
    msg = @queue.pop
    if !msg.nil?
      handle_message(msg) # impl of which does neat stuff
	  msg.delete
    else
      sleep 10
    end
  end
end

Depois de passar a mensagem para o método handle_message , eu posso excluí-la. Se nenhuma mensagem for encontrada, o encadeamento principal hiberna por 10 segundos. A linha !msg.nil? é igual a algo como msg != null em código Java. No Ruby, porém, até null é um objeto. Perguntar a um objeto se ele é do tipo nil (através da chamada de método nil? ) retorna uma variável booleana.


Conclusão

Como o AWS é uma oferta de serviços da Web, ele pode ser acessado e aproveitado por várias bibliotecas de plataformas. No Magnus, vemos a flexibilidade resultante: fui capaz de empurrar mensagens para uma fila SQS usando código Java, e então tirá-las com um pequeno programa do Ruby. Uma das maravilhas de uma arquitetura empregar filas é esse desacoplamento implícito dos componentes.

Assim como muitas vezes faz sentido hospedar um aplicativo da Web no GAE ou no Elastic Beanstalk da Amazon, também faz sentido tirar proveito de um sistema de mensagens em nuvem.O Amazon SQS alivia o fardo de instalar e manter um sistema de enfileiramento de mensagens. Você simplesmente cria uma fila e então solta e recupera mensagens nela. Deixe a Amazon se preocupar com o resto.

Recursos

Aprender

Obter produtos e tecnologias

  • Amazon Simple Queue Service: Leia a documentação de referência e seja apresentado ao Amazon SQS.
  • O framework Play: Descrito como uma estrutura Java desenvolvida por desenvolvedores da Web, o Play concentra-se na produtividade e tem como alvo arquiteturas RESTful.

Discutir

  • Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks, enquanto explora os blogs, fóruns, grupos e wikis orientados ao desenvolvedor.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Software livre, Cloud computing
ArticleID=648402
ArticleTitle=Desenvolvimento em Java 2.0: Sistema de mensagens baseado em nuvem com o Amazon SQS
publish-date=04192011