Storm + Kafka无法按预期进行并行处理

时间:2019-03-08 07:45:23

标签: apache-kafka apache-storm

我们在单个拓扑内的任务并行性方面存在问题。我们无法获得良好,流畅的处理速度。

我们正在使用Kafka和Storm构建具有不同拓扑的系统,在该系统中,数据是根据使用Kafka主题连接的一系列拓扑进行处理的。

我们正在使用Kafka 1.0.0和Storm 1.2.1

消息量很小,每天大约2000条消息,但是每个任务可能要花费一些时间。特别是一种拓扑可能需要花费可变的时间来处理每个任务,通常在1到20分钟之间。如果按顺序处理,则吞吐量不足以处理所有传入消息。所有拓扑和Kafka系统都安装在一台计算机上(16个内核,16 GB RAM)。

由于消息是独立的并且可以并行处理,因此我们尝试使用Storm并发功能来提高吞吐量。

为此,拓扑已配置如下:

  • 4名工人
  • 并行度提示设置为10
  • 从Kafka读取消息时,消息大小足够大,可以读取每条消息中的大约8个任务。
  • Kafka主题使用复制因子= 1,分区=10。

使用此配置,我们在此拓扑中观察到以下行为。

  • 通过Storm拓扑从卡夫卡读取大约7-8个任务(任务大小未固定),最大消息大小为128 kB。
  • 大约同时执行4-5个任务。工作或多或少地平均分配给工人。一些工人承担1个任务,其他工人承担2个并发处理。
  • 任务完成时,其余任务开始。
  • 仅剩下1-2个任务需要处理时,就会出现饥饿问题。其他工人会闲着直到所有任务完成。
  • 所有任务完成后,将确认消息并将其发送到下一个拓扑。
  • 从Kafka中读取了一个新批次,该过程再次开始。

我们有两个主要问题。首先,即使有4个工作程序和10个并行提示,也仅启动4-5个任务。其次,即使只有一项任务,在有待处理的工作时也不会启动任何批处理。

没有足够的工作要做的问题,因为我们一开始尝试插入2000个任务,所以有很多工作要做。

我们试图增加参数“ maxSpoutsPending”,希望拓扑将读取更多批并将它们同时排队,但是似乎它们是在内部进行流水线处理,而不是同时进行处理。

使用以下代码创建拓扑:

private static StormTopology buildTopologyOD() {
    //This is the marker interface BrokerHosts.
    BrokerHosts hosts = new ZkHosts(configuration.getProperty(ZKHOSTS));
    TridentKafkaConfig tridentConfigCorrelation = new TridentKafkaConfig(hosts, configuration.getProperty(TOPIC_FROM_CORRELATOR_NAME));

    tridentConfigCorrelation.scheme = new RawMultiScheme();
    tridentConfigCorrelation.fetchSizeBytes = Integer.parseInt(configuration.getProperty(MAX_SIZE_BYTES_CORRELATED_STREAM));

    OpaqueTridentKafkaSpout spoutCorrelator = new OpaqueTridentKafkaSpout(tridentConfigCorrelation);

    TridentTopology topology = new TridentTopology();

    Stream existingObject = topology.newStream("kafka_spout_od1", spoutCorrelator)
            .shuffle()
            .each(new Fields("bytes"), new ProcessTask(), new Fields(RESULT_FIELD, OBJECT_FIELD))
            .parallelismHint(Integer.parseInt(configuration.getProperty(PARALLELISM_HINT)));

    //Create a state Factory to produce outputs to kafka topics.
    TridentKafkaStateFactory stateFactory = new TridentKafkaStateFactory()
            .withProducerProperties(kafkaProperties)
            .withKafkaTopicSelector(new ODTopicSelector())
            .withTridentTupleToKafkaMapper(new ODTupleToKafkaMapper());

    existingObject.partitionPersist(stateFactory, new Fields(RESULT_FIELD, OBJECT_FIELD), new TridentKafkaUpdater(), new Fields(OBJECT_FIELD));

    return topology.build();
}

和配置创建为:

private static Config createConfig(boolean local) {
    Config conf = new Config();
    conf.setMaxSpoutPending(1); // Also tried 2..6
    conf.setNumWorkers(4);

    return conf;
}

我们是否可以通过增加并行任务的数量或/和在完成批处理时避免饥饿来提高性能?

1 个答案:

答案 0 :(得分:0)

我发现内森·马兹(Nathan Marz)在风暴用户中发现了old post,有关为Trident设置并行性:

  

我建议使用“名称”功能来命名流的各个部分   以便用户界面可以显示与各个部分对应的螺栓。

     

Trident将操作打包到尽可能少的螺栓中。此外,   除非您完成操作,否则它绝不会重新划分流   明确涉及重新分区(例如shuffle,groupBy,   partitionBy,全局聚合等)。三叉戟的财产   确保您可以控制事物的排序/半排序   被处理。因此,在这种情况下,groupBy之前的所有内容都必须   具有相同的并行度,否则三叉戟将不得不重新分区   流。而且由于您没有说过要流   重新分区,它无法做到这一点。您可以获得不同的并行度   通过引入重新分区来针对喷口   操作,就像这样:

     

stream.parallelismHint(1).shuffle()。each(...)。each(...)。parallelismHint(3).groupBy(...);

我认为您可能想为自己的喷口以及.each设置parallelismHint。

关于同时处理多个批处理,您很正确,这就是maxSpoutPending在Trident中的用途。尝试在Storm UI中检查您的最大出水口未决值是否实际被提取了。另外,请尝试为MasterBatchCoordinator启用调试日志记录。通过该日志记录,您应该能够知道是否同时有多个批次在飞行。

当您说不是同时处理多个批次时,您的意思是ProcessTask吗?请记住,Trident的属性之一是根据批次对状态更新进行排序。如果您有maxSpoutPending = 3并且批次1、2和3在飞行中,在写入批次1之前,Trident不会发射更多的批次进行处理,此时它将再发射一个。因此,即使完成了2和3的处理,速度较慢的批处理也会阻止更多的发光,它们必须等待1完成并被写入。

如果您不需要Trident的批处理和订购行为,则可以尝试使用常规Storm。

更多附带说明,但您可能需要考虑从storm-kafka迁移到storm-kafka-client。这个问题并不重要,但是如果不这样做,您将无法升级到Kafka 2.x,并且在迁移一堆状态之前会更加容易。