Kafka Streams简单Demo分析

Kafka Streams是在kafka基础之上的一套流式处理工具,通过Kafka Streams可以快速完成对存储在kafka cluster中的数据计算与处理。

Apache kafka官网提供了一个简单的word count的demo,实现了对不断输入的句子进行字数统计。整个过程是比较简单的,但是在阅读起Kafka Streams的代码时,一开始还是很头疼的。下面就简单介绍一下各部分代码的意思。

Properties props = new Properties();//streams的配置参数
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "myword-count");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092");
props.put(StreamsConfig.CACHE_MAX_BYTES_BUFFERING_CONFIG, 0);
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");

这部分是streams的一些配置参数,相比较kafka的producer与consumer,这里的配置还没什么特殊之处。

Kafka Streams中的核心是processor,所有的流式计算都是由processor完成的用户可以定义自己所需要的processor,但是在这个demo中仅仅使用提供的默认的processor就可以。

StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> source = builder.stream("mysentences");//使用默认的拓扑从myjavatopic中创建源
KTable<String,Long> counts=source.flatMapValues(new ValueMapper<String, Iterable<String>>() {
    @Override
    public Iterable<String> apply(String s) {
        return Arrays.asList(s.toLowerCase(Locale.getDefault()).split(" "));
    }
}).groupBy(new KeyValueMapper<String, String, String>() {
    @Override
    public String apply(String s, String s2) {
        return s2;
    }
}).count();

这段代码算是整个demo的核心了,前两句还比较好理解,就是进行简单的数据源配置。在省城source对象时,一开始看上去完全不知道在干什么。这里要一步步的看。

首先,最外层是一个KStream的flatMapValues的方法,这里的函数原型是

<VR> KStream<K, VR> flatMapValues(final ValueMapper<? super V, ? extends Iterable<? extends VR>> mapper);

不得不说,在Kafka Streams中泛型是无处不在。这里主要会涉及四种泛型

K,V:这一对是指原始流中的键值对;

KR,VR:这一对指的是结果流中的键值对。

在KStream找到了两个与上面这个方法非常相似的另外两个方法,现在把他们三个放在一起进行一个比较,这样能比较容易理解这几个方法的意思。

<KR, VR> KStream<KR, VR> flatMap(final KeyValueMapper<? super K, ? super V, ? extends Iterable<? extends KeyValue<? extends KR, ? extends VR>>> mapper);
<VR> KStream<K, VR> flatMapValues(final ValueMapper<? super V, ? extends Iterable<? extends VR>> mapper);
<VR> KStream<K, VR> flatMapValues(final ValueMapperWithKey<? super K, ? super V, ? extends Iterable<? extends VR>> mapper);

其实这里只有两个方法,flatMap和flatMapValues。

flatMap是将输入流中的每一条记录转成输出流中的0个或者多个记录。例如,输入流中的一条记录<K,V>经过flatMap后变成<K':V'>, <K'':V''>, ...成为了输出流中的多条记录。

而flatMapValues则不同,flatMapValues是将输入流中的一条记录转成输出流中有着相同Key值的0个或多个记录。例如,输入流中的一条记录<K,V>经过flatMapValues后变成了<K:V'>, <K:V''>, ...成为了输出流中相同K的多条记录。

上述的两种操作都是无状态的,即数据只要经过处理就好,不会依赖前后之间的状态。相应的还有一种操作是有状态的,例如后面会使用到的groupby操作,有状态的操作会将结果临时存在内存中,当达到某一特定条件后根据内存中的数据计算出最终结果。

上面的三个方法中第三个方法是对第二个方法的重载,两个主要区别在于参数类型不同,同时也可以看到第一个方法的参数类型与其余两个也是极其相似的。

public interface KeyValueMapper<K, V, VR>
public interface ValueMapper<V, VR>
public interface ValueMapperWithKey<K, V, VR>

kstream包中有三个类似的接口

其中ValueMapper是将一个value映射成一个新的value;

KeyValueMapper是将一个key-value键值对映射成一个新的key-value键值对,其中的key和value都是可以修改的

ValueMapperWithKey是将一个value映射成一个新的value与valueMapper的区别是ValueMapperWithKey会在apply方法中带着只读的Key

通过对这几个相似的方法和接口的对比,大致了解了各个方法和接口的功能。现在回到demo中,我们首先需要将原始流中的句子按词拆分,然后再统计各个词出现的次数。

第一步就是拆分词,这个过程中我们不关心原始流中的key值,因此选择flatMapValues,将原始流中一条记录的value值转换成输出流中以单词为value的多条记录。当然,如果使用flatMap也是可以的,但那样的话需要多处理一个无用的key;

将词进行拆分后,下一步就是要统计从历史记录开始到现在,各个单词的出现次数。这个过程又可以分为两步,groupBy和count,就类似SQL中的操作一样。groupBy操作会重新组织流中的数据,并且使用KeyValueMapper提供的值作为新Key,这个过程中Kafka Streams会在kafka cluster上生成一个叫做${applicationId}-XXX-repartition的新topic。最后count方法会根据组织好的key统计流中的出现次数。

Topology topology=builder.build();
System.out.println(topology.describe());

counts.toStream().to("mywordcount2", Produced.with(Serdes.String(),Serdes.Long()));
final KafkaStreams streams=new KafkaStreams(topology,props);
final CountDownLatch latch=new CountDownLatch(1);
Runtime.getRuntime().addShutdownHook(new Thread("streams-mywordcount-shutdown-hook"){
    @Override
    public void run() {
        streams.close();
        latch.countDown();
    }
});
try {
    streams.start();
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
    System.exit(1);
}
System.exit(0);

剩下的工作就是启动streams,并创建一个关闭钩子,保证我们的程序能够安全退出。

上面就是我对官网给出的demo的一个简单理解,里面还是有很多地方理解的不够彻底,还得在后续的开发中边用边学。

转载于:https://my.oschina.net/coderzhoutf/blog/1817414

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值