Google cloud platform 数据流自动缩放不会提高性能

Google cloud platform 数据流自动缩放不会提高性能,google-cloud-platform,google-cloud-dataflow,apache-beam,google-cloud-pubsub,Google Cloud Platform,Google Cloud Dataflow,Apache Beam,Google Cloud Pubsub,我正在构建一个数据流管道,它从pubsub读取数据并向第三方API发送请求。管道使用基于吞吐量的自动缩放 然而,当我对它进行负载测试时,在它自动调整为4个工作以赶上pubsub中的积压工作之后,似乎相同的工作负载在工作之间分布,但总体吞吐量没有显著增加 ^pubsub中未确认的邮件数。高峰是车辆停止进入的时候 ^从每个工作进程发送的字节数。峰值是第一个工人。随着更多的工作人员添加到池中,工作负载将被卸载,而不是每个人都承担更多的工作负载。CPU利用率看起来是一样的,初始工作线程的峰值利用率低

我正在构建一个数据流管道,它从pubsub读取数据并向第三方API发送请求。管道使用基于吞吐量的自动缩放

然而,当我对它进行负载测试时,在它自动调整为4个工作以赶上pubsub中的积压工作之后,似乎相同的工作负载在工作之间分布,但总体吞吐量没有显著增加

^pubsub中未确认的邮件数。高峰是车辆停止进入的时候

^从每个工作进程发送的字节数。峰值是第一个工人。随着更多的工作人员添加到池中,工作负载将被卸载,而不是每个人都承担更多的工作负载。CPU利用率看起来是一样的,初始工作线程的峰值利用率低于30%

^工人的历史产生了

这感觉要么是某个地方受到了限制,但我很难看到限制是什么。我每秒只收到不到300条消息,每条消息大约1kb

更新: 我对使用TextIO的批处理作业和使用PubSubIO的流式作业进行了另一轮比较,这两种作业都使用“n1-standard-8”机器,工人数量固定为15人。批处理作业上升到450个元素/秒,但流式作业仍以230个元素/秒达到峰值。看来限制来自源头。虽然我不确定限制是什么

更新2 下面是一个简单的代码片段来重现这个问题。您需要手动将工作数设置为1和5,并比较管道处理的元素数。您需要一个负载测试仪来高效地将消息发布到主题。

包调试;
导入java.io.IOException;
导入org.apache.beam.runners.dataflow.DataflowRunner;
导入org.apache.beam.runners.dataflow.options.DataflowPipelineOptions;
导入org.apache.beam.runners.dataflow.options.DataflowPipelineWorkerPoolOptions;
导入org.apache.beam.sdk.Pipeline;
导入org.apache.beam.sdk.PipelineResult;
导入org.apache.beam.sdk.io.gcp.pubsub.PubsubIO;
导入org.apache.beam.sdk.options.pipelineoptions工厂;
导入org.apache.beam.sdk.transforms.DoFn;
导入org.apache.beam.sdk.transforms.ParDo;
公共类调试管道{
@抑制警告(“串行”)
公共静态PipelineResult main(字符串[]args)引发IOException{
/*******************************************
*设置-生成选项。
********************************************/
DataflowPipelineOptions=PipelineOptionFactory.fromArgs(args).withValidation()
.as(DataflowPipelineOptions.class);
options.setRunner(DataflowRunner.class);
options.setAutoscalingAlgorithm(
DataflowPipelineWorkerPoolOptions.AutoscalingAlgorithmType.Throughts(基于U);
//自动缩放将在n/15和n个工作人员之间进行缩放,因此这里从1到15
选项。setMaxNumWorkers(15);
//250GB的默认值高得离谱,我们不需要对每个工人都这么多
选项。setDiskSizeGb(32);
//手动配置缩放(即1对5进行比较)
选项。setNumWorkers(5);
//调试管道
Pipeline=Pipeline.create(选项);
管道
.apply(publisubio.readStrings()
.fromSubscription(“您的订阅”))
//这是我真正关心的转换
//向某个第三方端点发送REST请求。
.apply(“sleep”,ParDo.of(new DoFn)(){
@过程元素
public void processElement(ProcessContext c)引发InterruptedException{
睡眠(500);
c、 输出(c.element());
}
}));
返回管道。run();
}
}

考虑到:

  • 从PubSubIO切换到TextIO没有任何改善
  • 从3名工人变为15名工人没有任何改善
  • 批量作业增加到450elements/s,但流式处理的峰值为230elements/s
  • 有一个转换将REST请求发送到第三方API,需要花费数小时的时间
  • 在测试中,取消转换将吞吐量从120个元素/秒增加到400个元素/秒
  • 问题似乎不在公众一边。据了解,您可能正在重载第三方API。客户机文档(而非第三方API)中解释了相同的效果:

    一个客户机可能有积压的消息,因为 它没有处理传入数据量的能力 消息,但网络上的另一个客户端确实具有该容量。 第二个客户机可以减少总体积压工作,但没有得到充分的支持 由于第一个客户端无法将其消息发送到 第二个客户足够快。这降低了总的失业率 正在处理,因为消息被卡在第一个客户端上

    创建积压工作的消息消耗内存、CPU和带宽 资源,因为客户端库继续扩展消息的 确认截止日期

    [……]

    更一般地说,流控制的需要表明消息是 以高于消费的速度发布。如果这 是持久状态,而不是消息卷中的尖峰,考虑 增加订阅服务器客户端实例和计算机的数量

    如果您只能使用PubSub来改进结果,并且您认为实现这一点的方法是延长元素的
    确认截止时间
    ,那么您可以通过访问和手动编辑订阅来测试它。要使用Java以编程方式完成此操作,请查看和文档,分别介绍如何管理订阅和更改
    ackDeadlineSeconds

    古董
    package debug;
    
    import java.io.IOException;
    
    import org.apache.beam.runners.dataflow.DataflowRunner;
    import org.apache.beam.runners.dataflow.options.DataflowPipelineOptions;
    import org.apache.beam.runners.dataflow.options.DataflowPipelineWorkerPoolOptions;
    import org.apache.beam.sdk.Pipeline;
    import org.apache.beam.sdk.PipelineResult;
    import org.apache.beam.sdk.io.gcp.pubsub.PubsubIO;
    import org.apache.beam.sdk.options.PipelineOptionsFactory;
    import org.apache.beam.sdk.transforms.DoFn;
    import org.apache.beam.sdk.transforms.ParDo;
    
    public class DebugPipeline {
        @SuppressWarnings("serial")
        public static PipelineResult main(String[] args) throws IOException {
    
            /*******************************************
             * SETUP - Build options.
             ********************************************/
    
            DataflowPipelineOptions options = PipelineOptionsFactory.fromArgs(args).withValidation()
                    .as(DataflowPipelineOptions.class);
            options.setRunner(DataflowRunner.class);
            options.setAutoscalingAlgorithm(
                    DataflowPipelineWorkerPoolOptions.AutoscalingAlgorithmType.THROUGHPUT_BASED);
            // Autoscaling will scale between n/15 and n workers, so from 1-15 here
            options.setMaxNumWorkers(15);
            // Default of 250GB is absurdly high and we don't need that much on every worker
            options.setDiskSizeGb(32);
            // Manually configure scaling (i.e. 1 vs 5 for comparison)
            options.setNumWorkers(5);
    
            // Debug Pipeline
            Pipeline pipeline = Pipeline.create(options);
            pipeline
                .apply(PubsubIO.readStrings()
                        .fromSubscription("your subscription"))
                // this is the transform that I actually care about. In production code, this will
                // send a REST request to some 3rd party endpoint.
                .apply("sleep", ParDo.of(new DoFn<String, String>() {
                    @ProcessElement
                    public void processElement(ProcessContext c) throws InterruptedException {
                        Thread.sleep(500);
                        c.output(c.element());
                    }
                }));
    
            return pipeline.run();
        }
    }