Usando Amazon SQS como sistema de mensagens (2/2)

Em post anterior, explicamos os conceitos por trás do Amazon Simple Queue Service, mais conhecido como SQS, e deixamos para um post futuro a demostração de exemplos sobre como postar e consumir mensagens de uma fila. E agora o post futuro finalmente chegou.

Como o próprio nome do serviço diz,  a utilização dos serviços SQS é bem simples e direto.

Vamos começar pelos pré-requisitos para trabalhar com uma fila específica, utilizando Java como linguagem e Maven para build.

Será necessário inicialmente adicionar a dependência correta ao seu projeto para que o SDK do SQS esteja disponível, como apresentado abaixo:

<dependency>
   <groupId>com.amazonaws</groupId>
   <artifactId>aws-java-sdk-sqs</artifactId>
   <version>1.10.56</version>
</dependency>

Note que esta dependência irá trazer diversas outras dependências da Amazon por transitividade. Mas não se assuste, é normal.

Agora precisamos descobrir a URL da fila para onde iremos enviar mensagem. Isto pode ser obtido direto do console SQS na AWS, conforme indicado na Figura 1, a seguir:

Figura 1 - Obtendo a url de uma fila
Figura 1 – Obtendo a url de uma fila

Note que a url da fila neste caso é https://sqs.us-west-1.amazonaws.com/536311044217/reset-password-queue.

Você precisará também de credenciais – access key e secret key – para se autenticar no SQS programaticamente. Estas credenciais devem ser fornecidas pelo responsável pelo gerenciamento de usuários no seu ambiente ou, caso você tenha este acesso, podem ser geradas pelo IAM, conforme instruções em [1].

Vamos agora ao código, começando por um exemplo comentado de como enviar uma mensagem:

public static void main(String[] args) {
		final String accessKey = "sua-access-key";
		final String secretKey = "sua-secret-key";

		/**
		 * Note que aqui estamos usando BasicAWSCredentials, onde as chaves são
		 * informadas programaticamente. Existem outras implementações de
		 * AWSCredentials que leem as credenciais de system properties ou de
		 * arquivos de configuração. Procure explorar estas alternativas.
		 */
		AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

		/**
		 * Aqui criamos o cliente do Amazon SQS com as credenciais.
		 */
		AmazonSQS amazonSqs = new AmazonSQSClient(credentials);
		/**
		 * Indicando a url onde queremos trabalhar
		 */
		amazonSqs.setRegion(Region.getRegion(Regions.US_WEST_1));

		/**
		 * URL da fila onde iremos colocar a mensagem
		 */
		final String QUEUE_URL = "https://sqs.us-west-1.amazonaws.com/536311044217/reset-password-queue";

		/**
		 * Mensagem propriamente dita
		 */
		final String message = "hello world";

		/**
		 * Criação do request para envio da mensagem
		 */
		SendMessageRequest request = new SendMessageRequest(QUEUE_URL, message);

		/**
		 * Envio da mensagem propriamente dito
		 */
		amazonSqs.sendMessage(request);

	}

Veja que o código é bem direto: criamos o client, construímos a mensagem e a enviamos.

Existem, entretanto, outras opções para envio de mensagens, úteis especialmente para trabalhar com grandes volumes. Procure explorar o método sendMessageBatch para envio de diversas mensagens simultaneamente.

E agora vamos ao código comentado para recebimento da mensagem enviada anteriormente.


 

	 final String accessKey = "sua-access-key";
	 final String secretKey = "sua-secret-key";

	 /**
	 * Repetimos o código para construção do client
	 */
	 AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
	 AmazonSQS amazonSqs = new AmazonSQSClient();
	 amazonSqs.setRegion(Region.getRegion(Regions.US_WEST_1));

	 /**
	 * URL da fila onde iremos buscar a mensagem
	 */
	 final String QUEUE_URL = "https://sqs.us-west-1.amazonaws.com/536311044217/reset-password-queue";

	 /**
	 * Criação do request para recebimento de mensagens
	 */
	 ReceiveMessageRequest request = new ReceiveMessageRequest(QUEUE_URL);

	 /**
	 * Limitando o numero de mensagens a serem retornadas Note que a própria
	 * Amazon estabelece um limite próprio de 10. Assim, configurar valores
	 * maiores do que 10 aqui não tem efeito.
	 */
	 request.setMaxNumberOfMessages(10);

	 /**
	 * Chamada do serviço de consulta. As mensagens retornadas por esta
	 * consulta ficarão invisíveis para outras chamdas durante o tempo
	 * configurado no Console SQS - o visibility timeout. Isto garante que
	 * eu tenha tempo suficiente para processar a mensagem sem que outra
	 * chamada a leia também.
	 */
	 ReceiveMessageResult result = amazonSqs.receiveMessage(request);

	 /**
	 * Tratando as mensagens
	 */
	 List messages = result.getMessages();
	 for (Message m : messages) {
	 System.err.println(m.getBody());

	 /**
	 * Uma vez processada a mensagem, precisamos apagá-la do SQS para
	 * que não seja consumida por outros processos
	 */
	 DeleteMessageRequest deleteRequest = new DeleteMessageRequest(QUEUE_URL, m.getReceiptHandle());
	 amazonSqs.deleteMessage(deleteRequest);
	 }

O ponto a chamar a atenção aqui é a chamada ao deleteMessage após o processamento da mensagem. Caso isto não seja feito, a mensagem ficará disponível novamente para leitura e poderá causar processamento duplicado. Assim, reforçamos  que caso o processamento da mensagem seja demorado, o visibility timeout seja configurado com um valor também alto para evitar problemas.

Bom, terminamos aqui a exemplificação das chamadas básicas do SQS e deixamos para o leitor a exploração de outros métodos e mesmo frameworks que integram com o SQS. Fica como recomendação o estudo e testes da integração Spring x AWS[2], pois facilitam ainda mais o uso da ferramenta.

Links

  1. http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSGettingStartedGuide/AWSCredentials.html
  2. Integração Spring x AWS: https://github.com/spring-projects/spring-integration-aws

Um Script Criador de Máquinas

No meu Post Anterior, expliquei alguns conceitos básicos para usarmos as ferramentas em Shell para a AWS. Nesse texto, dou umas idéias básicas de como criar um script que cria máquinas dentro do ambiente AWS (VPC/EC2).

Antes de mais nada, já aviso que talvez essa não seja a maneira mais fácil de criar um servidor na AWS, pois pode-se criar um servidor, deixá-lo configurado e gerar uma AMI que seria a base para futuras instalações. O objetivo aqui, é demonstrar o conceito. Você pode decidir posteriormente se é melhor ou não.

Além disso, outro ponto importante é que os comandos AWS shell serão relativamente poucos. O restante é puro Shell Script. No entanto, vale a pena analisarmos alguns trechos importantes, o mesmo, pois a criação e instalação de máquinas possui várias pegadinhas. Como ponto a favor, pode-se adaptar esse script para ambientes non-cloud ou servidores físicos de sua rede interna. Um conceito que passamos a adotar dentro da MATERA.

Inicialmente, vamos descrever nosso ambiente no servidor MASTER, onde ficará o script instalador. Aqui sugerimos alguns arquivos importantes.

  • amazon.defaults: arquivo com diversas definições de variáveis que serão usadas para a criação de máquinas.
  • create-machine.sh: Script de criação do computador na AWS (EC2).
  • install-machine.sh: Chamado pelo script anterior, fica em separado, caso precise ser chamado em separado (em caso de erros na execução do script anterior, ou de parâmetros de uso).
  • stage/ :subdiretório com as pastas e arquivos que serão copiados para o servidor criado. Dentro do mesmo, devem ficar tudo que não for default do servidor E que não seja instalado via RPM/yum. Exemplos são ajustes em arquivos de configuração, usuários que devem existir no servidor, chaves SSH, etc. Cada arquivo/pasta deve seguir a estrutura que estaria no rootdir do servidor a ser criado. ex: stage/etc/passwd, stage/etc/http/conf.d/ssl.conf, etc.
  • stage/tmp/postinstall.sh: Script customizado para seu servidor. Será chamado a partir do servidor criado, para finalizar a instalação do computador.

Bom, primeiramente vamos olhar o conteúdo do arquivo de amazon.defaults do seu ambiente:

# sobreescreve o rootbase 
ROOTBASE=/installers 
# regiao da maquina 
REGION=us-west-2 
# chave de acesso 
KEY=AWS_MASTER 
KEYFILE=$ROOTBASE/$KEY.pem 
# tipo do servidor 
SIZE=m3.medium 
# Security group 
SECURITYGROUPS=sg-fe5718cd 
# AMI a ser usada 
AMI=ami-b5a7ea85 
# Usuario para conexao 
RMTUSER=ec2-user

Note que são apenas definições de variáveis simples. Elas serão chamadas no script propriamente dito através do comando shell source (vulgo .). Vamos analisar partes essenciais de um script assim:

Pegar os parâmetros da linha de comando. O primeiro parâmetro deve ser o arquivo com os defaults do seu ambiente. O segundo será o nome da máquina, no console EC2.

DEFAULTS=$1
source $DEFAULTS
# Nome do servidor na AWS
 TAG=$2
#Arquivo com informacoes do servidor (apos criado)
 INFOFILE=$ROOTBASE/$TAG.info

Criação da máquina propriamente dita. O resultado do comando é jogado no arquivo .info.

#Cria a maquina
ec2-run-instances $AMI -t $SIZE -k $KEY -b $PART --region $REGION $SECGROUP > $INFOFILE
#Verifica qual o ID da instancia
INSTANCIA=`awk 'NR==2{print $2}' $INFOFILE`
#Cria a Tag
ec2-create-tags $INSTANCIA --tag "Name=$TAG"

O ip dinâmico fica arquivado dentro do arquivo .info. Você pode pegar esse dado usando suas ferramentas favoritas de texto (grep, cut, sed, aws, etc).

IP=`cat $INFOFILE|...`

Por fim, rodamos uma conexão para testar (e salvar a chave no known_hosts). Por fim, roda o comando install-machine.sh. Como dito, esse script fica em separado, para que possa ser executado em separado, em caso de problemas. Além disso, note um novo sleep que é usado para aguardar o boot desse servidor.

sleep 300
 # Aguarda um tempo, antes de começar a instalar
 # Faz uma conexao de testes
 $ssh -i $KEYFILE $RMTUSER@$IP id
# Roda o script de configuracao
 $ROOTBASE/install-machine.sh $DEFAULTS $IP

Agora bastar criarmos uma nova máquina:

./create-machine.sh ./amazon.defaults AWS-MATERA-TST1

Continuando, agora veremos detalhes de um script install-machine.sh:

Primeiro deve-se pegar os parâmetros novamente, pois como disse, o script pode ser executado separadamente:

Definimos onde está a estrutura a ser copiada e o arquivo temporário a ser enviado para o servidor.

# Algumas variaveis globais
# Localizacao do stage
STAGEBASE=$ROOTBASE/stage
# Arquivo de stage, sera usado para compactar e copiar o $STAGEBASE
STAGEFILE=/tmp/stage.tgz

Aqui geramos um arquivo .tgz do stage e copiamos o mesmo para o servidor. Após isso abrimos o .tgz lá.

# Faz a copia
cd $STAGEBASE
$tar  cfz $STAGEFILE .
$scp -q -i $KEYFILE $STAGEFILE $RMTUSER@$IP:$STAGEFILE
# abre a imagem
$ssh -t -i $KEYFILE -l $RMTUSER $IP "(cd /;sudo $tar xfz $STAGEFILE)"

Por fim rodamos um outro script, agora dentro do servidor instalado. Esse script está dentro do stage e pode ser customizado de acordo com as necessidades específicas do seu servidor. Na MATERA, cada tipo de servidor (appserver, bdserver, webserver, etc) tem seu próprio script:

# roda o postinstall
 echo -n "Rodar o script de de post-install. ok? [Y] "
 read RUNSCRIPT
 if [ "$RUNSCRIPT" = "y" -o "$RUNSCRIPT" = "Y" -o "$RUNSCRIPT" = "" ]; then
  $ssh -t -i $KEYFILE -l $RMTUSER $IP "sudo /tmp/postinstall.sh"
 fi

Vamos olhar um exemplo de script postinstall.sh.

Primeiramente, atualizamos o servidor com novos pacotes da AWS. Além disso, podemos instalar os pacotes que não vem por default e que nosso servidor possa precisar:

#!/bin/sh
# postinstall.sh: Roda comandos apos a instalacao
yum -y update
yum -y install emacs xinetd rrdtool sysstat

Devemos iniciar os serviços na máquina. Também devemos configurar os mesmos para subirem no boot do servidor, caso o mesmo tenha que ser reiniciado. Ex:

chkconfig --add tomcat
chkconfig tomcat on
chkconfig nrpe on
/etc/rc.d/init.d/xinetd start
/etc/rc.d/init.d/tomcat start

Por fim, não esqueça da limpeza da casa:

rm -f /tmp/stage.tgz
rm -f /tmp/postinstall.sh

Prontinho. Essas são ideias que devem ser lembradas ao se fazer um script nesse sentido.

A estratégia de crescimento da surpreendente Amazon

É muito difícil, ao analisar um mercado, fixar o momento exato em que uma tendência se torna realmente relevante. E, ao chamar a empresa Amazon de “surpreendente”, pressupõe-se que em algum momento houve uma surpresa: uma notícia de alto impacto, uma reversão de expectativas; o anúncio de um novo plano de negócios; ou, como é na maioria das vezes, uma conclusão, nova e importante, sobre um conjunto de fatos que eram – até então – aparentemente isolados.

Em novembro de 2011, a influente revista Wired publicou o artigo Jeff Bezos Owns the Web in More Ways Than You Think (que pode ser livremente traduzido como “Jeff Bezos manda na Web bem mais do que você pensa”), já querendo dizer que nem todo mundo havia atentado para as implicações do crescimento e da diversificação de negócios da Amazon.

amazon-warehouse-2
Centro de Distribuição da Amazon em Phoenix, Arizona – Estados Unidos.

Vamos comentar aspectos desse crescimento. Antes de começar, porém, podemos adiantar uma conclusão essencial: a Amazon é, hoje, uma das empresas mais importantes do mercado de tecnologia – no mesmo nível de Apple, Google e Microsoft.

Difícil imaginar que uma empresa que começou simplesmente vendendo livros na internet tenha chegado à posição de estar entre essas gigantes. Nem sempre os resultados foram tão espetaculares (basta lembrar os vários anos de prejuízos acumulados, no início do negócio), mas o seu sucesso é exemplar pela consistência e pelas abordagens inovadoras.

Quais os aspectos mais visíveis dessa estratégia e qual seu impacto no mercado?

  • Satisfação dos consumidores: o próprio Jeff Bezos não se cansa de reiterar sua “obsessão pelos consumidores”, preocupação essa que se reflete nos mínimos detalhes dos serviços e produtos; reclamações de consumidores sobre problemas em sites de e-commerce são comuns, mas é difícil encontrar consumidores insatisfeitos com a Amazon;
  • Simples de usar: durante muito tempo falou-se de “cloud computing”, que, por ser um novo paradigma de prestação de serviços em TI, trazia uma série de preocupações (segurança, administração, compliance, padrões etc); o serviço “Amazon Web Services”, lançado em 2002, tornou-se praticamente um sinônimo de “cloud computing” e permitiu que vários negócios migrassem de forma bem-sucedida para essa plataforma;
  • Preços competitivos: a empresa tem que trabalhar com margens muito pequenas e conciliar a questão dos custos operacionais com a qualidade dos serviços; mesmo apesar de ter ampliado consideravelmente a oferta de produtos e serviços com o passar dos anos, a empresa consegue competir com concorrentes de peso; como diz o próprio Jeff Bezos, “premium products at nonpremium prices”.

Aliás, seria Jeff Bezos o novo Steve Jobs?

.