La cola del bocadillo en el recreo
En la cantina de un instituto hay una sola cola. El alumno nuevo se pone al final y el cantinero atiende a quien está al principio.
Una cola de SQS hace lo mismo con mensajes. Una tienda deja cada pedido nuevo en la cola y un trabajador los coge de uno en uno. Esta lección sigue un mensaje desde que se envía hasta que se borra.
Al final de esta lección
Nota
Puedo decir qué le pasa a un mensaje desde que se envía hasta que se borra.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Cola | una fila de mensajes que esperan a ser tratados |
| Mensaje | un texto que espera en una cola |
| Recibir | pedir a la cola mensajes para trabajar con ellos |
| Visibility timeout | el tiempo que un mensaje recibido queda oculto |
| Receipt handle | el resguardo que permite borrar un mensaje recibido |
| Al menos una vez | un mensaje puede llegar dos veces, pero nunca ninguna |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Recibir te da un mensaje
send_message pone un texto en la cola. Su respuesta trae un MessageId. receive_message pide mensajes. Su respuesta trae una lista llamada Messages.
Cada mensaje tiene un Body, que es el texto, y un ReceiptHandle. Un cuerpo es solo texto. Muchas tiendas guardan JSON en él, y el trabajador lo vuelve diccionario con json.loads.
Ejemplo resuelto: una respuesta de receive
| Campo | Qué contiene | Ejemplo |
|---|---|---|
| Messages | una lista con un diccionario por mensaje | dos mensajes |
| MessageId | el id que dio la cola al enviar | m-1 |
| Body | el texto que se envió | {"pedido": 1, "producto": "bocadillo"} |
| ReceiptHandle | el resguardo para borrar esta recepción | h-1 |
| ApproximateReceiveCount | cuántas veces se recibió, en texto | "1" |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Leer una respuesta de receive
import json
answer = {
"Messages": [
{"MessageId": "m-1", "ReceiptHandle": "h-1",
"Body": "{\"pedido\": 1, \"producto\": \"bocadillo\"}",
"Attributes": {"ApproximateReceiveCount": "1"}},
{"MessageId": "m-2", "ReceiptHandle": "h-2", "Body": "dos",
"Attributes": {"ApproximateReceiveCount": "1"}},
]
}
for message in answer["Messages"]:
print(message["MessageId"], message["Body"])
order = json.loads(answer["Messages"][0]["Body"])
print("producto:", order["producto"])
# Cambia el bucle para que muestre también el ReceiptHandle.
Un mensaje recibido está oculto, no borrado
Recibir no quita el mensaje. Lo oculta durante el visibility timeout, que es de 30 segundos en la cola capturada. Otros trabajadores no pueden recibirlo mientras está oculto.
Cuando el trabajo termina, llama a delete_message con el ReceiptHandle. Solo entonces desaparece el mensaje. Si el trabajador se cae antes, el tiempo acaba y el mensaje vuelve a ser visible para otro.
Ejemplo resuelto: atributos de una cola
| Atributo | Valor | Significado |
|---|---|---|
| VisibilityTimeout | "30" | segundos que un mensaje recibido queda oculto |
| ApproximateNumberOfMessages | "3" | mensajes que esperan ser recibidos |
| ApproximateNumberOfMessagesNotVisible | "0" | mensajes ocultos tras una recepción |
| MessageRetentionPeriod | "345600" | segundos que se guarda un mensaje, cuatro días |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Oculto y luego visible otra vez
TIMEOUT = 30
received_at = {"m-1": 100}
def is_visible(message_id, now):
if message_id not in received_at:
return True
return now >= received_at[message_id] + TIMEOUT
for now in [100, 120, 130, 160]:
print(now, is_visible("m-1", now))
print("m-2 nunca recibido:", is_visible("m-2", 100))
# Cambia TIMEOUT a 60. ¿En qué momentos es visible m-1 ahora?
Un error frecuente
Advertencia
Mucha gente cree que recibir quita el mensaje. Solo lo oculta. Si nunca llamas a delete_message, el mensaje vuelve tras el tiempo y se trata otra vez. Bórralo cuando el trabajo esté hecho, nunca antes.
Al menos una vez significa que hay repeticiones
La cola guarda un mensaje hasta que se borra, así que un mensaje puede llegar dos veces. Un trabajador puede acabar el trabajo y caerse antes de borrar.
Una recepción también puede devolver menos mensajes de los pedidos, o ninguno. Una respuesta vacía no tiene la clave Messages, así que léela con .get. Escribe el trabajador para que una repetición no haga daño.
Saltarse una repetición
deliveries = ["m-1", "m-2", "m-1", "m-3"]
done = []
for message_id in deliveries:
if message_id in done:
print(message_id, "otra vez: sáltalo")
else:
done.append(message_id)
print(message_id, "primera vez: haz el trabajo")
empty = {}
print("mensajes:", len(empty.get("Messages", [])))
# Prueba con empty["Messages"]. ¿Qué sale mal?
Pruébalo en el laboratorio de nube
El laboratorio de nube es una copia de práctica de S3, SQS y DynamoDB. Pulsa Iniciar el laboratorio en la barra y luego Ejecutar en cada orden de abajo. Usan la línea de comandos real de AWS, así que las mismas palabras sirven en una cuenta real. Tras borrar, vuelve a preguntar a la cola: no imprime nada, la cola parece vacía. Este paso es para practicar. La tarea de abajo sigue siendo Python simple, así que puedes hacerla sin el laboratorio.
Pon una clave inventada y la dirección
export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_DEFAULT_REGION=us-east-1 AWS_ENDPOINT_URL=http://cloud:4566Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Crea una cola
Q=$(aws sqs create-queue --queue-name orders --query QueueUrl --output text)Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Envía un mensaje
aws sqs send-message --queue-url $Q --message-body "order 1"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Recíbelo y guarda la respuesta
aws sqs receive-message --queue-url $Q | tee reply.jsonSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Guarda su resguardo, el ReceiptHandle
H=$(grep -o '"ReceiptHandle": "[^"]*"' reply.json | cut -d'"' -f4)Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Borra el mensaje
aws sqs delete-message --queue-url $Q --receipt-handle "$H"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.