kafka Streaming

1.概述

Kafka Streams是一个用于构建应用程序和微服务的客户端库,其中的输入和输出数据存储在Kafka集群中。它结合了在客户端编写和部署标准Java和Scala应用程序的简单性,以及Kafka服务器端集群技术的优点。

  1. Topology(拓扑):表示一个流计算任务,等价于MapReduce中的job。不同的是MapReduce的job作业最终会停止,但是ToPology会一直运行在内存中,除非人工关闭该Topology。
  2. Stream:它代表了一个无限的,不断更新的Record数据集。流是有序的,可存放和容错的不可变数据记录序列,其中数据记录定义为键值对。
  3. States:可以持久化存放流计算状态结果,可以用以容错和故障恢复。
  4. Time:
    1. Event Time(事件时间)
    2. Processing Time(处理时间)
    3. Ingestion Time(摄入时间)

注意:所谓的流处理就是通过Topology编织程序对Stream中的Record元素的处理的逻辑/流程。

2.架构

kafka Streams通过构建kafka生产者和消费者并利用kafka的本机功能来提供数据并行性,分布式协调,容错和操作简便性,从而简化了应用程序开发。

kafka Streaming_第1张图片
kafka的消息分区用于存储和传递消息,kafka Streams对数据进行分区以进行处理。kafka Streams 使用Partition和Task的概念作为基于kafka Topic分区的并行模型的逻辑单元。在并行化的背景下,kafka Streams和kafka之间有着密切的联系:

  1. 每个Stream分区都是完全有序的数据记录序列,并映射到kafka Topic分区。
  2. Stream中的数据记录映射到该Topic的kafka消息。
  3. 数据记录的key决定了kafka Streams中数据的分区,即数据如何路由到Topic的特定分区。

3.任务的并行度

Kafka Streams基于应用程序的输入流分区创建固定数量的Task,每个任务(Task)分配来自输入流的分区列表(即Kafka主题)。分区到任务的分配永远不会改变,因此每个任务都是应用程序的固定平行单元。然后,任务可以根据分配的分区实例化自己的处理器拓扑; 它们还为每个分配的分区维护一个缓冲区,并从这些记录缓冲区一次一个地处理消息。因此,流任务可以独立并行地处理,无需人工干预。
在这里插入图片描述
用户可以启动多个KafkaStream实例,这样等价启动了多个Stream Tread,每个Thread处理1~n个Task。一个Task对应一个分区,因此Kafka Stream流处理的并行度不会超越Topic的分区数。需要值得注意的是Kafka的每个Task都维护这自身的一些状态,线程之间不存在状态共享和通信。因此Kafka在实现流处理的过程中扩展是非常高效的。

在这里插入图片描述

4.容错

Kafka Streams构建于Kafka本地集成的容错功能之上。 Kafka分区具有高可用性和复制性;因此当流数据持久保存到Kafka时,即使应用程序失败并需要重新处理它也可用。 Kafka Streams中的任务利用Kafka消费者客户端提供的容错功能来处理故障。如果任务运行的计算机故障了,Kafka Streams会自动在其余一个正在运行的应用程序实例中重新启动该任务。

此外,Kafka Streams还确保local state store也很有力处理故障容错。对于每个state store,Kafka Stream维护一个带有副本changelog的Topic,在该Topic中跟踪任何状态更新。这些changelog Topic也是分区的,该分区和Task是一一对应的。如果Task在运行失败并Kafka Stream会在另一台计算机上重新启动该任务,Kafka Streams会保证在重新启动对新启动的任务的处理之前,通过重播相应的更改日志主题,将其关联的状态存储恢复到故障之前的内容。

5.使用kafka Streming完成单词计数的实时统计

注:创建kafka Streaming Topology有两种方式
1.low-level:Processor API
2.high-level:Kafka Streams DSL(DSL:提供了通用的数据操作算子,如:map, filter, join, and aggregations等)

5.1 创建Maven工程,引入相关依赖


    org.apache.kafka
    kafka-streams
    2.2.0

5.2 编写自定义处理器

package com.hw.lowlevel;

import org.apache.kafka.connect.data.Timestamp;
import org.apache.kafka.streams.processor.Processor;
import org.apache.kafka.streams.processor.ProcessorContext;
import org.apache.kafka.streams.processor.PunctuationType;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * @aurhor:fql
 * @date 2019/8/31 16:22
 * @type:
 */
public class WordCountProcessor implements Processor {

    //流处理的中间结果(状态)
    private Map wordcount;
    private  ProcessorContext context;

    /**
     * 初始化方法
     * @param context  上下文环境信息
     */
    @Override
    public void init(final ProcessorContext context) {
        wordcount=new HashMap<>();
        this.context=context;
        //将单词统计的结果发送给下游的sink输出
        this.context.schedule(Duration.ofSeconds(1), PunctuationType.STREAM_TIME,(Timestamp->{
            wordcount.forEach((word,num)->{
                context.forward(word,num);
            });
        }));
    }

    /**
     *
     * @param key
     * @param value 持续输入的流数据 ,一行英文短语
     */

    @Override
    public void process(Integer key, String value) {
        String[] words = value.split(" ");
        for (String word : words) {

            Long num = wordcount.getOrDefault(word, 0L);
            num++;
            wordcount.put(word,num);
        }
        //标记处理结束
        this.context.commit();
    }

    @Override
    public void close() {

    }
}

5.3 编写Stream

package com.hw.lowlevel;
import org.apache.kafka.common.serialization.LongSerializer;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.common.serialization.StringSerializer;
import org.apache.kafka.streams.KafkaStreams;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.streams.Topology;

import java.util.Properties;

/**
 * @aurhor:fql
 * @date 2019/9/1 9:17
 * @type: 实时的单词计数
 */
public class WordCountApplication {
    public static void main(String[] args) {
        //1.创建一个配置对象
        Properties properties = new Properties();
        properties.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG,"node1:9092,node2:9092,node3:9092");
        //key的序列化和反序列化
        properties.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.Integer().getClass());
        //vaule反序列化和反序列化
        properties.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG,Serdes.String().getClass());
        //应用的ID=拉取消费组的ID
        properties.put(StreamsConfig.APPLICATION_ID_CONFIG,"wordCount");
        //流处理应用 开启的线程数量
        properties.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG,2);
        //手动绘制拓扑
        Topology topology = new Topology();
        topology.addSource("s1","hw");//指定数据源
        topology.addProcessor("p1",()->new WordCountProcessor(),"s1");//指定处理器
        topology.addSink("k1","t8",new StringSerializer(),new LongSerializer(),"p1");
        //初始化kafka流处理任务
        KafkaStreams kafkaStreams = new KafkaStreams(topology, properties);

        //运行流处理程序
        kafkaStreams.start();

    }
}

5.4 进行测试

  1. 启动数据源输入测试数据
    在这里插入图片描述
  2. 启动订阅t8查看统计结果
    启动指令
bin/kafka-console-consumer.sh --bootstrap-server node1:9092,node2:9092,node3:9092 \
    --topic t8 \  (指定输出的主题)
    --from-beginning \
    --formatter kafka.tools.DefaultMessageFormatter \
    --property print.key=true \
    --property print.value=true \
    --property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer \  (输出结果的反序列化)
    --property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer

kafka Streaming_第2张图片

你可能感兴趣的:(Kafka)