我们拥有基于GCP PubSub的发布者和订阅者系统。订户处理单个消息的时间很长,大约1分钟。我们已经将订阅者的确认截止期限设置为600秒(10分钟)(最大),以确保pubsub不会过早开始重新交付,因为基本上我们在这里运行时间很长。
我看到了PubSub的这种行为。当代码发送确认,并且监视器确认PubSub确认请求已被接受并且确认本身以成功状态完成时,未确认消息的总数仍然相同。
图表上的指标显示相同的总和,计数和均值聚合对齐器。在上面的图片中,对齐器是平均值,没有启用减速器。
我正在使用@ 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)。
问题本身-发生了什么,为什么收到确认消息后未确认消息的数量没有减少?
答案 0 :(得分:1)
要引用the documentation的报价,您正在使用的subscription / num_undelivered_messages指标是“订阅中未确认的消息(即积压消息)数。每60秒进行一次采样。采样后,数据在长达120秒的时间内不可见。”
您不应期望该指标在确认消息后立即下降。此外,听起来好像您正试图将pubsub用于恰好一次的传递情况,试图确保不会再次传递该消息。 Cloud Pub / Sub不提供这些语义。它至少提供一次语义。换句话说,即使您已经收到一个值,确认了该值,收到了ack响应,并且看到指标从1下降到0,对于同一工人或另一个工人,仍然有可能并正确地接收到该消息的精确副本。 。尽管在实践中这不太可能,但是您应该专注于构建具有重复容忍能力的系统,而不是尝试确保成功完成确认,这样就不会重新发送邮件。