簡體   English   中英

如何使用Python中的boto庫獲取Amazon SQS隊列中的所有消息?

[英]How to get all messages in Amazon SQS queue using boto library in Python?

我正在開發一個應用程序,其工作流程通過使用boto在SQS中傳遞消息來管理。

我的SQS隊列逐漸增長,我無法檢查它應該包含多少元素。

現在我有一個守護進程定期輪詢隊列,並檢查我是否有一組固定大小的元素。 例如,考慮以下“隊列”:

q = ["msg1_comp1", "msg2_comp1", "msg1_comp2", "msg3_comp1", "msg2_comp2"]

現在我想在某個時間點檢查隊列中是否有“msg1_comp1”,“msg2_comp1”和“msg3_comp1”,但我不知道隊列的大小。

查看API后,您似乎只能獲得1個元素或隊列中固定數量的元素,但不是全部:

>>> rs = q.get_messages()
>>> len(rs)
1
>>> rs = q.get_messages(10)
>>> len(rs)
10

答案中提出的建議是在循環中獲取10個消息,直到我什么也得不回來,但SQS中的消息具有可見性超時,這意味着如果我從隊列中輪詢元素,它們將不會被真正刪除,它們只會在短時間內隱形。

是否有一種簡單的方法來獲取隊列中的所有消息,而不知道有多少消息?

在while循環中調用q.get_messages(n)

all_messages=[]
rs=q.get_messages(10)
while len(rs)>0:
    all_messages.extend(rs)
    rs=q.get_messages(10)

此外, dump也不會支持超過10條消息

def dump(self, file_name, page_size=10, vtimeout=10, sep='\n'):
    """Utility function to dump the messages in a queue to a file
    NOTE: Page size must be < 10 else SQS errors"""

我一直在使用AWS SQS隊列來提供即時通知,因此我需要實時處理所有消息。 以下代碼將幫助您有效地將(所有)消息出列並在刪除時處理任何錯誤。

注意:要從隊列中刪除消息,您需要刪除它們。 我正在使用更新的boto3 AWS python SDK,json庫和以下默認值:

import boto3
import json

region_name = 'us-east-1'
queue_name = 'example-queue-12345'
max_queue_messages = 10
message_bodies = []
aws_access_key_id = '<YOUR AWS ACCESS KEY ID>'
aws_secret_access_key = '<YOUR AWS SECRET ACCESS KEY>'
sqs = boto3.resource('sqs', region_name=region_name,
        aws_access_key_id=aws_access_key_id,
        aws_secret_access_key=aws_secret_access_key)
queue = sqs.get_queue_by_name(QueueName=queue_name)
while True:
    messages_to_delete = []
    for message in queue.receive_messages(
            MaxNumberOfMessages=max_queue_messages):
        # process message body
        body = json.loads(message.body)
        message_bodies.append(body)
        # add message to delete
        messages_to_delete.append({
            'Id': message.message_id,
            'ReceiptHandle': message.receipt_handle
        })

    # if you don't receive any notifications the
    # messages_to_delete list will be empty
    if len(messages_to_delete) == 0:
        break
    # delete messages to remove them from SQS queue
    # handle any errors
    else:
        delete_response = queue.delete_messages(
                Entries=messages_to_delete)

我的理解是,SQS服務的分布式特性幾乎使您的設計無法工作。 每次調用get_messages時,您都會與另一組服務器通信,這些服務器將包含部分但不是全部的消息。 因此,不可能“不時地檢查”以設置特定的消息組是否准備就緒,然后只接受這些消息。

您需要做的是持續輪詢,在到達時收集所有消息,並將它們本地存儲在您自己的數據結構中。 每次成功獲取后,您都可以檢查數據結構,以查看是否收集了一整套消息。

請記住,消息無序到達,並且一些消息被傳遞兩次,因為刪除必須傳播到所有SQS服務器,但后續獲取請求有時會超過刪除消息。

我在cronjob中執行此操作

from django.core.mail import EmailMessage
from django.conf import settings
import boto3
import json

sqs = boto3.resource('sqs', aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
         aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
         region_name=settings.AWS_REGION)

queue = sqs.get_queue_by_name(QueueName='email')
messages = queue.receive_messages(MaxNumberOfMessages=10, WaitTimeSeconds=1)

while len(messages) > 0:
    for message in messages:
        mail_body = json.loads(message.body)
        print("E-mail sent to: %s" % mail_body['to'])
        email = EmailMessage(mail_body['subject'], mail_body['message'], to=[mail_body['to']])
        email.send()
        message.delete()

    messages = queue.receive_messages(MaxNumberOfMessages=10, WaitTimeSeconds=1)

類似下面的代碼應該可以解決問題。 對不起,這是在C#中,但轉換為python應該不難。 字典用於清除重復項。

    public Dictionary<string, Message> GetAllMessages(int pollSeconds)
    {
        var msgs = new Dictionary<string, Message>();
        var end = DateTime.Now.AddSeconds(pollSeconds);

        while (DateTime.Now <= end)
        {
            var request = new ReceiveMessageRequest(Url);
            request.MaxNumberOfMessages = 10;

            var response = GetClient().ReceiveMessage(request);

            foreach (var msg in response.Messages)
            {
                if (!msgs.ContainsKey(msg.MessageId))
                {
                    msgs.Add(msg.MessageId, msg);
                }
            }
        }

        return msgs;
    }

注意:這不是對問題的直接回答。 相反,它是對@TimothyLiu的答案的補充,假設最終用戶使用Boto包(又名Boto2)而不是Boto3 此代碼是他的答案中提到的delete_messages調用的“Boto-2- delete_messages


Boto (2)調用delete_message_batch(messages_to_delete) ,其中messages_to_delete是一個帶有鍵的dict對象:對應於id值: receipt_handle對返回

AttributeError:'dict'對象沒有屬性'id'。

似乎delete_message_batch需要一個Message類對象; 如果您一次刪除超過10條“消息”,則復制delete_message_batchBoto源並允許它使用非Message對象(ala boto3 )也會失敗。 所以,我不得不使用以下解決方法。

這里打印代碼

 from __future__ import print_function import sys from itertools import islice def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) @static_vars(counter=0) def take(n, iterable, reset=False): "Return next n items of the iterable as same type" if reset: take.counter = 0 take.counter += n bob = islice(iterable, take.counter-n, take.counter) if isinstance(iterable, dict): return dict(bob) elif isinstance(iterable, list): return list(bob) elif isinstance(iterable, tuple): return tuple(bob) elif isinstance(iterable, set): return set(bob) elif isinstance(iterable, file): return file(bob) else: return bob def delete_message_batch2(cx, queue, messages): #returns a string reflecting level of success rather than throwing an exception or True/False """ Deletes a list of messages from a queue in a single request. :param cx: A boto connection object. :param queue: The :class:`boto.sqs.queue.Queue` from which the messages will be deleted :param messages: List of any object or structure with id and receipt_handle attributes such as :class:`boto.sqs.message.Message` objects. """ listof10s = [] asSuc, asErr, acS, acE = "","",0,0 res = [] it = tuple(enumerate(messages)) params = {} tenmsg = take(10,it,True) while len(tenmsg)>0: listof10s.append(tenmsg) tenmsg = take(10,it) while len(listof10s)>0: tenmsg = listof10s.pop() params.clear() for i, msg in tenmsg: #enumerate(tenmsg): prefix = 'DeleteMessageBatchRequestEntry' numb = (i%10)+1 p_name = '%s.%i.Id' % (prefix, numb) params[p_name] = msg.get('id') p_name = '%s.%i.ReceiptHandle' % (prefix, numb) params[p_name] = msg.get('receipt_handle') try: go = cx.get_object('DeleteMessageBatch', params, BatchResults, queue.id, verb='POST') (sSuc,cS),(sErr,cE) = tup_result_messages(go) if cS: asSuc += ","+sSuc acS += cS if cE: asErr += ","+sErr acE += cE except cx.ResponseError: eprint("Error in batch delete for queue {}({})\\nParams ({}) list: {} ".format(queue.name, queue.id, len(params), params)) except: eprint("Error of unknown type in batch delete for queue {}({})\\nParams ({}) list: {} ".format(queue.name, queue.id, len(params), params)) return stringify_final_tup(asSuc, asErr, acS, acE, expect=len(messages)) #mdel #res def stringify_final_tup(sSuc="", sErr="", cS=0, cE=0, expect=0): if sSuc == "": sSuc="None" if sErr == "": sErr="None" if cS == expect: sSuc="All" if cE == expect: sErr="All" return "Up to {} messages removed [{}]\\t\\tMessages remaining ({}) [{}]".format(cS,sSuc,cE,sErr) 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM