我有一个简单的 shell 脚本,可以连接到 GCP 并尝试从主题中提取 Pub/Sub 消息。
启动时,它会检查是否存在任何消息,如果存在则执行一个简单的操作,然后确认消息并循环。
看起来是这样的:
while [ 1 ]
do
gcloud pubsub subscriptions pull...
// Do something
gcloud pubsub subscriptions ack ...
done
随机它不会拉取消息:它们留在队列中并且不会被拉取。
所以我们尝试在获取消息时添加一个 while 循环,例如 5 re-try 以避免这些问题工作得更好但不完美。我也觉得有点破旧...
这个问题发生在其他项目上,从脚本 shell 迁移到 Java(出于其他一些原因),我们使用了拉订阅,现在它在这些项目上完美运行!
我们可能做错了什么,但我不知道是什么......
我读到有时 gcloud 拉取的消息比 pubsub 队列中的消息少:
https://cloud.google.com/sdk/gcloud/reference/pubsub/subscriptions/pull
但它必须至少拉取一个......在我们的例子中,没有消息被拉取而是随机的。
这里有什么需要改进的地方吗?
答案 0 :(得分:0)
一般来说,依靠使用 gcloud
检索消息并对其进行处理的 shell 脚本并不是使用 Cloud Pub/Sub 的有效方式。值得注意的是,pull 中没有返回的消息并不表示没有消息;这只是意味着在拉取请求的截止日期之前无法返回消息。 gcloud subscriptions pull
命令将 returnImmediately
属性(请参阅 pull documentation 中的信息)设置为 true
,这基本上意味着如果内存中没有可以快速访问的消息,则不会消息将被返回。此属性已弃用,不应设置为 true
,因此这可能是我们需要在 gcloud
中探索更改的内容。
最好使用 client libraries 来编写订阅者,以设置流并不断检索消息。如果您打算仅定期运行它,那么您可以编写一个作业来读取消息并在未收到消息并关闭后等待一段时间。同样,这并不能保证所有可用的消息都会被消费,但在大多数情况下确实如此。
Java 中的此版本如下所示:
import com.google.cloud.pubsub.v1.AckReplyConsumer;
import com.google.cloud.pubsub.v1.MessageReceiver;
import com.google.pubsub.v1.ProjectSubscriptionName;
import com.google.pubsub.v1.PubsubMessage;
import java.util.concurrent.atomic.AtomicLong;
import org.joda.time.DateTime;
/** A basic Pub/Sub subscriber for purposes of demonstrating use of the API. */
public class Subscriber implements MessageReceiver {
private final String PROJECT_NAME = "my-project";
private final String SUBSCRIPTION_NAME = "my-subscription";
private com.google.cloud.pubsub.v1.Subscriber subscriber;
private AtomicLong lastReceivedTimestamp = new AtomicLong(0);
private Subscriber() {
ProjectSubscriptionName subscription =
ProjectSubscriptionName.of(PROJECT_NAME, SUBSCRIPTION_NAME);
com.google.cloud.pubsub.v1.Subscriber.Builder builder =
com.google.cloud.pubsub.v1.Subscriber.newBuilder(subscription, this);
try {
this.subscriber = builder.build();
} catch (Exception e) {
System.out.println("Could not create subscriber: " + e);
System.exit(1);
}
}
@Override
public void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) {
// Process message
lastReceivedTimestamp.set(DateTime.now().getMillis());
consumer.ack();
}
private void run() {
subscriber.startAsync();
while (true) {
long now = DateTime.now().getMillis();
long currentReceived = lastReceivedTimestamp.get();
if (currentReceived > 0 && ((now - currentReceived) > 30000)) {
subscriber.stopAsync();
break;
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("Error while waiting for completion: " + e);
}
}
System.out.println("Subscriber has not received message in 30s. Stopping.");
subscriber.awaitTerminated();
}
public static void main(String[] args) {
Subscriber s = new Subscriber();
s.run();
System.exit(0);
}
}