Python 3.x 谷歌云同步拉

Python 3.x 谷歌云同步拉,python-3.x,google-cloud-platform,publish-subscribe,google-cloud-pubsub,Python 3.x,Google Cloud Platform,Publish Subscribe,Google Cloud Pubsub,我有一个主题和一个订阅,有多个订阅者。我的应用程序场景是,我希望处理不同订阅者上的消息,同时处理特定数量的消息。表示首先假设正在处理8条消息,然后如果一条消息处理完成,则在确认已处理消息后,下一条消息应取自主题,同时注意在任何订阅者上未发现重复消息,并且每次8条消息应在后台处理 为此,我使用了max_messages=8的同步拉取方法,但下一次拉取是在所有消息处理完成后完成的。所以我们已经创建了自己的调度器,在这个调度器中,同时8个进程应该在后台运行,并且一次提取1条消息,但仍然是在所有8条消息

我有一个主题和一个订阅,有多个订阅者。我的应用程序场景是,我希望处理不同订阅者上的消息,同时处理特定数量的消息。表示首先假设正在处理8条消息,然后如果一条消息处理完成,则在确认已处理消息后,下一条消息应取自主题,同时注意在任何订阅者上未发现重复消息,并且每次8条消息应在后台处理

为此,我使用了max_messages=8的同步拉取方法,但下一次拉取是在所有消息处理完成后完成的。所以我们已经创建了自己的调度器,在这个调度器中,同时8个进程应该在后台运行,并且一次提取1条消息,但仍然是在所有8条消息处理完成之后,下一条消息被传递

这是我的密码:

    #!/usr/bin/env python3

    import logging
    import multiprocessing
    import time
    import sys
    import random
    from google.cloud import pubsub_v1

    project_id = 'xyz'
    subscription_name = 'abc'

    NUM_MESSAGES = 4
    ACK_DEADLINE = 50
    SLEEP_TIME = 20

    multiprocessing.log_to_stderr()
    logger = multiprocessing.get_logger()
    logger.setLevel(logging.INFO)

    def worker(msg):
        logger.info("Received message:{}".format(msg.message.data))
        random_sleep = random.randint(200,800)
        logger.info("Received message:{} for {} sec".format(msg.message.data, random_sleep))
        time.sleep(random_sleep)

    def message_puller():
        subscriber = pubsub_v1.SubscriberClient()
        subscription_path = subscriber.subscription_path(project_id, subscription_name)
        while(True):
            try:
                response = subscriber.pull(subscription_path, max_messages=1)
                message = response.received_messages[0]
                msg = message
                ack_id = message.ack_id
                process = multiprocessing.Process(target=worker, args=(message,))
                process.start()
                while process.is_alive():
                    # `ack_deadline_seconds` must be between 10 to 600.
                    subscriber.modify_ack_deadline(subscription_path,[ack_id],ack_deadline_seconds=ACK_DEADLINE)
                    time.sleep(SLEEP_TIME)
                # Final ack.
                subscriber.acknowledge(subscription_path, [ack_id])
                logger.info("Acknowledging message: {}".format(msg.message.data))
    except Exception as e:
        print (e)
        continue

    def synchronous_pull():
        p = []
        for i in range(0,NUM_MESSAGES):
            p.append(multiprocessing.Process(target=message_puller))

        for i in range(0,NUM_MESSAGES):
            p[i].start()

        for i in range(0,NUM_MESSAGES):
            p[i].join()

    if __name__ == '__main__':
        synchronous_pull()
此外,有时subscriber.pull不提取任何消息,即使while循环始终为True。它给我错误的感觉 列表索引(0)超出范围 得出结论,subscriber.pull不拉入消息,即使消息与主题有关,但过了一段时间,它开始拉入消息。为什么会这样


我尝试过异步拉取和流控制,但在多个订阅服务器上发现重复消息。如果有任何其他方法可以解决我的问题,请让mi知道。提前感谢。

谷歌云PubSub至少保证一次。这意味着,这些消息可能会被传递多次。要解决这个问题,您需要制定您的程序/系统

您有多个订阅者,每个订阅者拉取8条消息。
为避免同一消息被多个订阅者处理,
acknowledge
在任何订阅者提取该消息并继续进一步处理该消息后,立即确认该消息,而不是在消息的整个处理过程结束时确认该消息

另外,当队列中没有消息时,不要连续运行主脚本,而要使用
sleep
保持一定的时间

我有一个类似的代码,我使用了同步拉,除了我没有使用并行处理

代码如下:

PubSubHandler-处理Pubsub相关操作的类

从google.cloud导入pubsub_v1
从google.api_core.exceptions导入死线
类别PubSubHandler:
def uuu init uuuu(self,subscriber_config):
self.project\u name=subscriber\u config['project\u name']
self.subscriber\u name=subscriber\u config['subscriber\u name']
self.subscriber=pubsub_v1.SubscriberClient()
self.subscriber\u path=self.subscriber.subscription\u path(self.project\u name,self.subscriber\u name)
def pull_消息(自身、消息数):
尝试:
响应=self.subscriber.pull(self.subscriber\u路径,最大消息数=消息数)
received_messages=response.received_messages
除死线外,如e:
收到的_消息=[]
打印('没有消息导致错误')
返回收到的消息
def确认消息(自身、消息ID):
如果len(消息ID)>0:
self.subscriber.acknowledge(self.subscriber\u路径、消息\u ID)
返回真值
Utils-用于util方法的类

导入json
类别UTIL:
定义初始化(自):
通过
def解码_数据_到_json(自解码_数据):
尝试:
解码的_数据=解码的_数据。替换(“,”)
json_data=json.load(解码的_数据)
返回json_数据
例外情况除外,如e:
引发异常('解析json时出错')
定义原始数据到utf(自身、原始数据):
尝试:
解码数据=原始数据。解码('utf8')
返回解码的数据
例外情况除外,如e:
引发异常(“转换为UTF时出错”)
或浏览器-主脚本


导入时间
导入json
导入日志记录
从utils导入utils
从db_连接导入DbHandler
从pub_sub_handler导入PubSubHandler
类或隔离器:
定义初始化(自):
self.MAX\u NUM\u消息=2
self.SLEEP\u TIME=10
self.util_methods=Utils()
self.pub\u sub\u handler=PubSubHandler(订户配置)
def主_处理器(自身):
至确认标识=[]
pull\u messages=self.pub\u sub\u handler.pull\u messages(self.MAX\u NUM\u messages)
如果len(拉取消息)<1:
self.SLEEP\u TIME=1
打印('队列中没有消息')
返回
logging.info('队列中的消息')
self.SLEEP\u TIME=10
对于拉式消息中的消息:
原始数据=message.message.data
尝试:
解码数据=self.util方法。原始数据到utf(原始数据)
json_data=self.util_methods.decoded_data_to_json(decoded_data)
打印(json_数据)
例外情况除外,如e:
logging.error(e)
到\u ack\u id.append(message.ack\u id)
如果self.pub\u sub\u handler.ack\u消息(到\u ack\u id):
打印('已确认的消息\u ID')
如果名称=“\uuuuu main\uuuuuuuu”:
orecestrator=或Estrator()
打印('接收数据..')
尽管如此:
orecestrator.main_handler()
时间.睡眠(orecestrator.睡眠时间)

Google PubSub确保每条消息至少发送一次。在某些情况下,您会多次收到相同的消息。在这种情况下,您的程序需要表现出幂等性。感谢您的回复。但在我的场景中,主题上大约有100条消息,每条消息的处理时间约为4到5小时。1。如果我先确认而不进行处理,则下一条消息将在订阅服务器上出现。如果任何进程崩溃,则我们希望在下一个或同一订阅服务器上再次处理该消息。2。