Google Cloud PubSub无法确认消息

时间:2019-02-08 17:13:27

标签: node.js google-cloud-platform google-cloud-pubsub stackdriver

我们拥有基于GCP PubSub的发布者和订阅者系统。订户处理单个消息的时间很长,大约1分钟。我们已经将订阅者的确认截止期限设置为600秒(10分钟)(最大),以确保pubsub不会过早开始重新交付,因为基本上我们在这里运行时间很长。

我看到了PubSub的这种行为。当代码发送确认,并且监视器确认PubSub确认请求已被接受并且确认本身以成功状态完成时,未确认消息的总数仍然相同。

enter image description here

图表上的指标显示相同的总和,计数和均值聚合对齐器。在上面的图片中,对齐器是平均值,没有启用减速器。

我正在使用@ google-cloud / pubsub Node.js库。已经尝试了不同的版本(0.18.1、0.22.2、0.24.1),但是我想问题不在其中。

以下类可用于检查。

TypeScript 3.1.1,节点8.x.x-10.x.x

import { exponential, Backoff } from "backoff";

const pubsub = require("@google-cloud/pubsub");

export interface IMessageHandler {
    handle (message): Promise<void>;
}

export class PubSubSyncListener {
    private readonly client;

    private listener: Backoff;

    private runningOperations: Promise<unknown>[] = [];

    constructor (
        private readonly handler: IMessageHandler,
        private readonly options: {
            /**
             * Maximal messages number to be processed simultaniosly.
             * Listener will try to keep processing number as close to provided value
             * as possible.
             */
            maxMessages: number;
            /**
             * Formatted full subscrption name /projects/{projectName}/subscriptions/{subscriptionName}
             */
            subscriptionName: string;
            /**
             * In milliseconds
             */
            minimalListenTimeout?: number;
            /**
             * In milliseconds
             */
            maximalListenTimeout?: number;
        }
    ) {
        this.client = new pubsub.v1.SubscriberClient();

        this.options = Object.assign({
            minimalListenTimeout: 300,
            maximalListenTimeout: 30000
        }, this.options);
    }

    public async listen () {
        this.listener = exponential({
            maxDelay: this.options.maximalListenTimeout,
            initialDelay: this.options.minimalListenTimeout
        });

        this.listener.on("ready", async () => {
            if (this.runningOperations.length < this.options.maxMessages) {
                const [response] = await this.client.pull({
                    subscription: this.options.subscriptionName,
                    maxMessages: this.options.maxMessages - this.runningOperations.length
                });

                for (const m of response.receivedMessages) {
                    this.startMessageProcessing(m);
                }
                this.listener.reset();
                this.listener.backoff();
            } else {
                this.listener.backoff();
            }
        });

        this.listener.backoff();
    }

    private startMessageProcessing (message) {
        const index = this.runningOperations.length;

        const removeFromRunning = () => {
            this.runningOperations.splice(index, 1);
        };

        this.runningOperations.push(
            this.handler.handle(this.getHandlerMessage(message))
                .then(removeFromRunning, removeFromRunning)
        );
    }

    private getHandlerMessage (message) {
        message.message.ack = async () => {
            const ackRequest = {
                subscription: this.options.subscriptionName,
                ackIds: [message.ackId]
            };

            await this.client.acknowledge(ackRequest);
        };

        return message.message;
    }

    public async stop () {
        this.listener.reset();
        this.listener = null;
        await Promise.all(
            this.runningOperations
        );
    }
}

这基本上是消息异步提取和立即确认的部分实现。因为提出的解决方案之一是使用同步拉动。

如果我没有记错问题的症状,我在Java存储库中发现了类似的报告问题。

https://github.com/googleapis/google-cloud-java/issues/3567

这里的最后一个细节是,确认似乎对少量请求有效。如果我在pubsub中触发单个消息然后立即对其进行处理,则未传递的消息数会减少(由于之前只有一条消息而下降为0)。

问题本身-发生了什么,为什么收到确认消息后未确认消息的数量没有减少?

1 个答案:

答案 0 :(得分:1)

要引用the documentation的报价,您正在使用的subscription / num_undelivered_messages指标是“订阅中未确认的消息(即积压消息)数。每60秒进行一次采样。采样后,数据在长达120秒的时间内不可见。

您不应期望该指标在确认消息后立即下降。此外,听起来好像您正试图将pubsub用于恰好一次的传递情况,试图确保不会再次传递该消息。 Cloud Pub / Sub不提供这些语义。它至少提供一次语义。换句话说,即使您已经收到一个值,确认了该值,收到了ack响应,并且看到指标从1下降到0,对于同一工人或另一个工人,仍然有可能并正确地接收到该消息的精确副本。 。尽管在实践中这不太可能,但是您应该专注于构建具有重复容忍能力的系统,而不是尝试确保成功完成确认,这样就不会重新发送邮件。