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

时间:2018-04-14 08:33:16

标签: python docker google-cloud-platform google-compute-engine google-cloud-pubsub

在我的场景中,我使用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消息,所以{m} creating a template用于以下两种方式之一使用auto-scaling的计算引擎:

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 behavior

更新我找到了nice explanation by Gregor Hohpe,是2015年企业集成模式一书的合着者。实际上我的观察是错误的,但观察到的副作用是真实的。

  

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

Copyright: Gregor Hohpe, co-author of the book Enterprise Integration Patterns. 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利用率是否仍然很强且处理尚未完成。
    • 处理后确认消息。在这种情况下,您需要在处理该消息时修改该特定消息的确认截止日期。自上次修改以来,一定不能有一个代码块中断截止日期。
  4. 尚未考虑的可能解决方案:

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

2 个答案:

答案 0 :(得分:1)

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

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

2)让你的工作流程提取请求,而不是将它们推送给工人。

请参阅

中“推送和拉送”下的“负载平衡”部分

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

答案 1 :(得分:0)

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参数可用于控制   消息被拉出。设置相对保守   默认情况下,以防止“消息ho积”-客户出现这种情况   提取大量消息,但处理速度不够快   使其“饿死”其他邮件客户。增加这些   设置可能会导致不使用   处理时间长。

此外,您可以控制以下订阅的ack_deadline_secondshttps://cloud.google.com/pubsub/docs/reference/rpc/google.pubsub.v1#google.pubsub.v1.Subscription