Storm Kafka Integration (0.10.x+)官方文档翻译:storm与kafka整合

Storm Kafka Integration (0.10.x+)

兼容性

Apache Kafka版本0.10以上

向kafka写数据作为拓扑的一部分

你可以创建一个org.apache.storm.kafka.bolt.KafkaBolt的实例,并将其作为一个组件添加到你的拓扑上,或者如果你正在使用trident你可以使用
org.apache.storm.kafka.trident.TridentState, org.apache.storm.kafka.trident.TridentStateFactory
org.apache.storm.kafka.trident.TridentKafkaUpdater.

你需要实现下面两个接口:

TupleToKafkaMapperTridentTupleToKafkaMapper

这些接口有两个方法定义:

    K getKeyFromTuple(Tuple/TridentTuple tuple);
    V getMessageFromTuple(Tuple/TridentTuple tuple);

顾名思义,这些方法被调用映射一个tuple到一个kafka key和kafka message。
如果你只需要一个字段作为key,一个字段作为value,那么你可以使用提供的FieldNameBasedTupleToKafkaMapper.java实现。

  • KafkaBolt里,如果使用默认构造函数构造FieldNameBasedTupleToKafkaMapper,则实现始终会查找具有字段名称“key”和“message”的字段,以实现向后兼容性的原因。
    或者,您也可以使用非默认构造函数指定不同的key和message字段。
  • TridentKafkaState中,您必须指定key和message的字段名称,因为没有默认构造函数。
    在构造FieldNameBasedTupleToKafkaMapper的实例时应指定这些。
KafkaTopicSelectortrident KafkaTopicSelector

这个接口只有一个方法

    public interface KafkaTopicSelector { 
        String getTopics(Tuple/TridentTuple tuple); 
    } 

这个接口的实现应该返回要发布tuple的key/message映射的主题。您可以返回一个null,该消息将被忽略。如果你有一个静态主题名称,那么你可以
使用DefaultTopicSelector.java并在构造函数中设置主题的名称
FieldNameTopicSelector 和 FieldIndexTopicSelector可以被使用来选择一个topic应该去发布一个tuple到哪。(select the topic should to publish a tuple to.)
用户只需要在tuple本身中指定topic名称的字段名称或字段索引。
当topic名称未找到时,Field * TopicSelector将会将消息写入默认topic。请保证默认的topic已经被创建

指定Kafka生产者属性

您可以通过调用KafkaBolt.withProducerProperties()TridentKafkaStateFactory.withProducerProperties()来提供Storm拓扑中的所有生产者属性。
生产者的重要的配置属性包括:

  • metadata.broker.list
  • request.required.acks
  • producer.type
  • serializer.class

这些也被定义在org.apache.kafka.clients.producer.ProducerConfig

使用通配符topic匹配(Using wildcard kafka topic match)

您可以通过添加以下配置来进行通配符主题匹配

Config config = new Config(); config.put("kafka.topic.wildcard.match",true);

之后你可以制定一个通配符主题去匹配。例如clickstream.*.log. 这将会匹配如下所有的流 clickstream.my.log, clickstream.cart.log 等等。

综上:

for the bolt:

        TopologyBuilder builder = new TopologyBuilder();

        Fields fields = new Fields("key", "message");
        FixedBatchSpout spout = new FixedBatchSpout(fields, 4,
                    new Values("storm", "1"),
                    new Values("trident", "1"),
                    new Values("needs", "1"),
                    new Values("javadoc", "1")
        );
        spout.setCycle(true);
        builder.setSpout("spout", spout, 5);
        //set producer properties.
        Properties props = new Properties();
        props.put("bootstrap.servers", "localhost:9092");
        props.put("acks", "1");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        KafkaBolt bolt = new KafkaBolt()
                .withProducerProperties(props)
                .withTopicSelector(new DefaultTopicSelector("test"))
                .withTupleToKafkaMapper(new FieldNameBasedTupleToKafkaMapper());
        builder.setBolt("forwardToKafka", bolt, 8).shuffleGrouping("spout");

        Config conf = new Config();

        StormSubmitter.submitTopology("kafkaboltTest", conf, builder.createTopology());

For Trident:

        Fields fields = new Fields("word", "count");
        FixedBatchSpout spout = new FixedBatchSpout(fields, 4,
                new Values("storm", "1"),
                new Values("trident", "1"),
                new Values("needs", "1"),
                new Values("javadoc", "1")
        );
        spout.setCycle(true);

        TridentTopology topology = new TridentTopology();
        Stream stream = topology.newStream("spout1", spout);

        //set producer properties.
        Properties props = new Properties();
        props.put("bootstrap.servers", "localhost:9092");
        props.put("acks", "1");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        TridentKafkaStateFactory stateFactory = new TridentKafkaStateFactory()
                .withProducerProperties(props)
                .withKafkaTopicSelector(new DefaultTopicSelector("test"))
                .withTridentTupleToKafkaMapper(new FieldNameBasedTupleToKafkaMapper("word", "count"));
        stream.partitionPersist(stateFactory, fields, new TridentKafkaUpdater(), new Fields());

        Config conf = new Config();
        StormSubmitter.submitTopology("kafkaTridentTest", conf, topology.build());

从kafka读数据(Spouts)

配置

spout的实现使用KafkaSpoutConfig类来配置。这个类使用Build模式,可以通过调用其中一个Builds构造函数或通过调用KafkaSpoutConfig类中的静态方法构建器来启动。
创建构建器的构造函数或静态方法需要几个key values(稍后可以更改),但是启动一个spout所需的最小配置。
bootstrapServers与Kafka Consumer Property “bootstrap.servers”是相同的。
spout将消耗的主题可以是特定topic名称(1个或更多)的集合或正则表达式Pattern,它指定匹配该正则表达式的任何主题将被使用。

在构造函数的情况下,您可能还需要指定键解串器和值解串器。这是为了通过使用Java泛型来保证类型安全。默认值为StringDeserializer
可以通过调用setKeyDeserializer 和/或 setValueDeserializer来覆盖。
如果这些设置为null,代码将回退到kafka属性中设置的内容,但是最好在这里明确表示,再次使用泛型来维护类型安全性。

有几个关键配置要注意

  • setFirstPollOffsetStrategy:允许您设置从哪里开始使用数据.这在故障恢复和首次启动spout的情况下都被使用。允许的值包括:
    • EARLIEST:无论以前的提交如何,kafka spout会轮询从分区的第一个偏移开始的记录
    • LATEST:无论以前的提交如何,kafka spout轮询(具有大于分区中最后一个偏移量的)偏移量的记录
    • UNCOMMITTED_EARLIEST(默认):kafka spout从最后提交的偏移量(如果有的话)中轮询记录,如果没有提交任何偏移量,则表现为EARLIEST
    • UNCOMMITTED_LATEST:kafka spout从最后提交的偏移量(如果有的话)中轮询记录,如果没有提交任何偏移量,则表现为LATEST
  • setRecordTranslator:允许您修改spout如何将Kafka Consume Record转换为tuple,以及将发布该元组的流。
    默认情况下,”topic”, “partition”, “offset”, “key” 和 “value”将被发送到“默认”流。
    如果要根据主题将条目输出到不同的流,则storm提供ByTopicRecordTranslator。有关如何使用这些的更多示例,请参见下文。
  • setProp:可以用来设置没有方便方法的kafka属性。
  • setGroupId:让你设置kafka消费者组属性“group.id”的id。
  • setSSLKeystore 和 setSSLTruststore:允许您配置SSL身份验证。
用法示例

API是用java 8 lambda表达式写的,它可以与java7及以下版本配合使用。

创建一个简单的不安全Spout

以下将消费所有的发布到“topic”的事件,并发送他们到MyBolt有着字段”topic”, “partition”, “offset”, “key”, “value”。

final TopologyBuilder tp = new TopologyBuilder(); 
tp.setSpout("kafka_spout", new KafkaSpout<>(KafkaSpoutConfig.builder("127.0.0.1:" + port, "topic").build()), 1); 
tp.setBolt("bolt", new myBolt()).shuffleGrouping("kafka_spout"); 

通配符topics
通配符主题将从指定代理列表中存在的所有主题消耗,并匹配该模式。所以在下面的例子中,”topic”, “topic_foo” and “topic_bar”将都会匹配”topic.*”,
但是”not_my_topic”不匹配。

final TopologyBuilder tp = new TopologyBuilder();
tp.setSpout("kafka_spout", new KafkaSpout<>(KafkaSpoutConfig.builder("127.0.0.1:" + port, Pattern.compile("topic.*")).build()), 1);
tp.setBolt("bolt", new myBolt()).shuffleGrouping("kafka_spout");
Multiple Streams(多个流)

这使用了java 8 lambda表达式。

final TopologyBuilder tp = new TopologyBuilder();
//By default all topics not covered by another rule, but consumed by the spout will be emitted to "STREAM_1" as "topic", "key", and "value" 
//默认情况下,所有未被其他规则覆盖的主题,但是由spout消耗的所有主题将被发送到“STREAM_1”作为"topic", "key", and "value"
ByTopicRecordTranslator byTopic = new ByTopicRecordTranslator<>( (r) -> new Values(r.topic(), r.key(), r.value()), new Fields("topic", "key", "value"), "STREAM_1"); 
//For topic_2 all events will be emitted to "STREAM_2" as just "key" and "value" 
byTopic.forTopic("topic_2", (r) -> new Values(r.key(), r.value()), new Fields("key", "value"), "STREAM_2");

tp.setSpout("kafka_spout", new KafkaSpout<>(KafkaSpoutConfig.builder("127.0.0.1:" + port, "topic_1", "topic_2", "topic_3").build()), 1);
tp.setBolt("bolt", new myBolt()).shuffleGrouping("kafka_spout", "STREAM_1"); 
tp.setBolt("another", new myOtherBolt()).shuffleGrouping("kafka_spout", "STREAM_2");
Trident
final TridentTopology tridentTopology = new TridentTopology();
final Stream spoutStream = tridentTopology.newStream("kafkaSpout",
    new KafkaTridentSpoutOpaque<>(KafkaSpoutConfig.builder("127.0.0.1:" + port, Pattern.compile("topic.*")).build()))
      .parallelismHint(1)

Trident不支持多个流,并将忽略为输出设置的任何流。然而,如果每个输出主题的字段不相同,它将抛出异常,而不会继续。

Custom RecordTranslators(自定义RecordTranslators)(高级)

在大多数情况下,内置的SimpleRecordTranslator和ByTopicRecordTranslator应该能覆盖您的用例。
如果您遇到需要定制的情况,则本文档将介绍如何正确执行此操作,以及一些不太明显的类。

适用的要点是使用ConsumerRecord并将其转换为可以发出的List 。不明显的是如何告诉spout将其发射到特定的流。为此,
您将需要返回一个org.apache.storm.kafka.spout.KafkaTuple的实例。这提供了一个routedTo方法,它将说明tuple应该去哪个特定的流。
例如:return new KafkaTuple(1, 2, 3, 4).routedTo("bar");
Will cause the tuple to be emitted on the “bar” stream(将导致元组在“bar”流中发出)

在编写自定义record translators时要小心,因为就像在storm spout中,它需要自我一致。stream方法应该返回一组完整的流,这个转换器将会尝试发送。

另外getFieldsFor应为每个流返回一个有效的Fields对象。
如果您正在为Trident执行此操作,则值必须位于通过应用该流的Fields对象中的每个字段返回的List中,否则trident可能会抛出异常。

手动分区控制(高级)

默认情况下,Kafka将自动将分区分配给当前的一组分支。它处理很多事情,但在某些情况下,您可能需要手动分配分区。

当spout go down 并重新启动时,这可能会导致更少的churn(搅拌),但是如果没有完成,可能会导致很多问题。
这都可以通过子类化Subscription来处理,我们有几个实现,您可以查看有关如何执行此操作的示例。
ManualPartitionNamedSubscriptionManualPartitionPatternSubscription。请使用这些或实现你自己的时候要小心。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值