繁体   English   中英

Google PubSub和自动扩展计算引擎实例(Python)

[英]Google PubSub and auto-scaling Compute Engine instances (Python)

在我的场景中,我正在使用PubSub安排任务。 这比在Google Compute Engine中的Docker容器内运行的Python脚本消耗的消息多达2.000个PubSub消息。 该脚本使用PubSub消息。

每条消息的处理大约是30秒到5分钟。 因此,确认截止日期为600秒(10分钟)。

from google.cloud import pubsub_v1
from google.cloud.pubsub_v1.subscriber.message import Message

def handle_message(message: Message):
    # do your stuff here (max. 600sec)
    message.ack()
    return

def receive_messages(project, subscription_name):

    subscriber = pubsub_v1.SubscriberClient()
    subscription_path = subscriber.subscription_path(project, subscription_name)

    flow_control = pubsub_v1.types.FlowControl(max_messages=5)
    subscription = subscriber.subscribe(subscription_path, flow_control=flow_control)

    future = subscription.open(handle_message)

    # Blocks the thread while messages are coming in through the stream. Any
    # exceptions that crop up on the thread will be set on the future.
    # The subscriber is non-blocking, so we must keep the main thread from
    # exiting to allow it to process messages in the background.
    try:
        future.result()
    except Exception as e:
        # do some logging
        raise

因为我正在处理这么多PubSub消息,所以我正在为计算引擎创建一个模板 ,它使用以下两种方式之一进行自动缩放

gcloud compute instance-groups managed create my-worker-group \
  --zone=europe-west3-a \
  --template=my-worker-template \
  --size=0

gcloud beta compute instance-groups managed set-autoscaling my-worker-group \
  --zone=europe-west3-a \
  --max-num-replicas=50 \
  --min-num-replicas=0 \
  --target-cpu-utilization=0.4

gcloud beta compute instance-groups managed set-autoscaling my-worker-group \
  --zone=europe-west3-a \
  --max-num-replicas=50 \
  --min-num-replicas=0 \
  --update-stackdriver-metric=pubsub.googleapis.com/subscription/num_undelivered_messages \
  --stackdriver-metric-filter="resource.type = pubsub_subscription AND resource.label.subscription_id = my-pubsub-subscription" \
  --stackdriver-metric-single-instance-assignment=10

到现在为止还挺好。 选项一扩展到大约8个实例,而第二个选项将启动最大实例数。 现在我发现有些奇怪的事情发生了,这就是我在这里发帖的原因。 也许你可以帮帮我?!

消息重复:似乎每个实例中的PubSub服务(计算引擎内的docker容器内的Python脚本)读取一批消息(~10),有点像缓冲区并将它们提供给我的代码。 看起来所有同时旋转的实例将读取所有相同的消息(2.000的前10个),并将开始处理相同的内容。 在我的日志中,我看到大多数消息由不同的机器处理3次。 我期待PubSub知道某些用户是否缓冲了10条消息,以便另一个用户将缓冲10条不同的消息,而不是相同的消息。

确认截止日期:由于缓冲,缓冲区末尾的消息(假设消息8或9)必须在缓冲区中等待,直到前面的消息(消息1到7)被处理完毕。 等待时间加上其自身处理时间的总和可能会超过600秒的超时。

负载平衡:因为每台机器都会缓冲这么多消息,所以只有少数实例会消耗负载,而其他实例则完全空闲。 对于使用PubSub stackdriver指标的scaling-option 2,会发生这种情况。

人们告诉我,我需要使用Cloud SQL或其他方法实现手动同步服务,其中每个实例都指示它正在工作的消息,以便其他实例不会启动相同的操作。 但我觉得这不可能是真的 - 因为那时我不知道PubSub是什么。

pubsub行为

更新:我发现了Gregor Hohpe的一个很好的解释 ,他是2015年企业集成模式一书的合着者。实际上我的观察是错误的,但观察到的副作用是真实的。

Google Cloud Pub / Sub API实际上实现了发布 - 订阅频道和竞争消费者模式。 Cloud Pub / Sub的核心是一个经典的Publish-Subscribe Channel,它向单个订阅者发布一条消息。 这种模式的一个优点是添加订阅者是无副作用的,这是发布 - 订阅频道有时被认为比点对点频道更松散耦合的一个原因,点对点频道向一个订户传递消息。 将消费者添加到点对点渠道会导致竞争消费者,从而产生强烈的副作用。

版权:Gregor Hohpe,“企业集成模式”一书的合着者。 2015年。

我观察到的副作用是关于每个订阅者(订阅相同订阅,点对点==竞争消费者)的消息缓冲和消息流控制。 当前版本的Python Client Lib包装了PubSub REST API(和RPC)。 如果使用该包装器,则无法控制:

  • 一台VM上启动了多少个容器; 如果CPU尚未充分利用,则可以启动多个容器
  • 一次从订阅中提取多少消息(缓冲); 根本没有控制权
  • 在容器内部启动了多少个用于处理拉出消息的线程; 如果值低于固定值,则flow_control(max_messages)无效。

我们观察到的副作用是:

  1. 一个消费者一次拉出大量消息(大约100到1.000)并将它们排队到其客户端缓冲区中。 因此,根据自动扩展规则启动的所有其他VM都不会收到任何消息,因为所有消息都在前几个VM的队列中
  2. 如果消息进入确认截止日期,则会将消息重新传递到同一VM或任何其他VM(或docker容器)。 因此,您需要在处理消息时修改确认截止日期。 处理开始时,截止时间计数器开始。
  3. 假设消息的处理是一个长时间运行的任务(例如机器学习),你可以
    • 事先确认消息,但如果没有其他消息等待,这将导致VM被自动缩放规则关闭。 该规则不关心CPU利用率是否仍然很强且处理尚未完成。
    • 处理后确认消息。 在这种情况下,您需要在处理该消息时修改该特定消息的确认截止日期。 自上次修改以来,一定不能有一个代码块中断截止日期。

尚未研究的可能解决方案:

  • 使用Java客户端库,因为它提供了更好的拉动和消费消息控制
  • 使用Python客户端库的底层API调用和类
  • 构建一个协调竞争消费者的同步存储

我认为有两种主要方法可以解决这个问题。

1)不要直接推送到您的工作进程,而是推送到负载均衡器。

要么

2)让您的工作流程提取请求而不是将其推送给工作人员。

请参阅“推送和拉出”中的“负载平衡”部分

https://cloud.google.com/pubsub/docs/subscriber

Python客户端库有很多配置选项: https//googleapis.github.io/google-cloud-python/latest/pubsub/subscriber/api/client.html#google.cloud.pubsub_v1.subscriber.client。 Client.subscribe

特别是,您想要查看flow_controlscheduler 重要报价:

flow_control参数可用于控制提取消息的速率。 默认设置是相对保守的,以防止“消息囤积” - 客户端提取大量消息但无法足够快地处理它们导致其“消息”消息的其他客户端。 增加这些设置可能会导致不需要很长时间处理的消息的吞吐量更快。

此外,您还可以控制订阅的ack_deadline_secondshttpsack_deadline_seconds

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM