一、kafka 介绍 && kafka-client

一、kafka 介绍

1.1、kafka 介绍

Kafka 是一个分布式消息引擎与流处理平台,经常用做企业的消息总线、实时数据管道,有的还把它当做存储系统来使用。
早期 Kafka 的定位是一个高吞吐的分布式消息系统,目前则演变成了一个成熟的分布式消息引擎,以及流处理平台。

Kafka 主要起到削峰填谷(缓冲)、系统解构以及冗余的作用,主要特点有:

  • 高吞吐、低延时:这是 Kafka 显著的特点,Kafka 能够达到百万级的消息吞吐量,延迟可达毫秒级;
  • 持久化存储:Kafka 的消息最终持久化保存在磁盘之上,提供了顺序读写以保证性能,并且通过 Kafka 的副本机制提高了数据可靠性。
  • 分布式可扩展:Kafka 的数据是分布式存储在不同 broker 节点的,以 topic 组织数据并且按 partition 进行分布式存储,整体的扩展性都非常好。
  • 高容错性:集群中任意一个 broker 节点宕机,Kafka 仍能对外提供服务。

使用消息队列的好处:
解耦、冗余(每个分区都有副本)、提高扩展性、灵活性 & 峰值处理能力、可恢复性(有副本)、顺序保证、缓冲、异步通信

1.2、kafka术语

  • 生产者(Producer):

    • 向 broker 发布消息的应用程序。
    • 生产者也负责选择发布到Topic上的哪一个分区。最简单的方式从分区列表中轮流选择。也可以根据某种算法依照权重选择分区。
  • 消费者(Consumer):

    • 从消息队列中获取消息的客户端应用程序。

    • 一个 topic 可以让若干个消费者进行消费,若干个消费者组成一个 Consumer Group 即消费组,一条消息只能被消费组中一个 Consumer 消费。

    • 假如所有的消费者都在一个组中,那么这就变成了 queue 模型。 假如所有的消费者都在不同的组中,那么就完全变成了发布-订阅模型。

    • 更通用的,我们可以创建一些消费者组作为逻辑上的订阅者。每个组包含数目不等的消费者,一个组内多个消费者可以用来扩展性能和容错。如下图所示:

    • 消费者
    • 2个 kafka 集群托管4个分区(P0-P3),2个消费者组,消费组 A 有2个消费者实例,消费组 B 有4个。

    • kafka 中消费者组有两个概念:队列:消费者组(consumer group)允许消费者组成员瓜分处理。发布订阅:允许你广播消息给多个消费者组(不同名)。

    • 传统的消息有两种模式:队列和发布订阅。

    • 在队列模式中,消费者池从服务器读取消息(每个消息只被其中一个读取),优点是允许多个消费者瓜分处理数据,这样可以扩展处理。;

    • 发布订阅模式:消息广播给所有的消费者。允许你广播数据到多个消费者,由于每个订阅者都订阅了消息,所以没办法缩放处理。

  • broker:

    • Kafka 实例,多个 broker 组成一个 Kafka 集群,Kafka 以集群方式运行,集群中每个服务器称为 broker。
    • 通常一台机器部署一个 Kafka 实例,一个实例挂了不影响其他实例
  • 主题(Topic):

    • 一组消息的归纳(代表不同的业务,如超市办会员,付款)。
    • 服务端消息的逻辑存储单元。一个 topic 通常包含若干个 Partition 分区。
  • 分区(Partition):

    • 一个 Topic 中的消息数据按照多个分区组织,分区是 Kafka 消息队列组织的最小单位,一个分区可以看作是一个队列。
    • 分布式存储在各个 broker 中, 实现发布与订阅的负载均衡。
    • 若干个分区可以被若干个 Consumer 同时消费,达到消费者高吞吐量。
    • 单个 partition 有序,整体无序,整体有序就将数据都放到一个 partition 中,但是效率极低。
    • 每个分区有一个 leader,零或多个 follower。Leader 处理此分区的所有的读写请求,而follower被动的复制数据。如果leader宕机,其它的一个follower会被推举为新的leader。
    • 一台服务器可能同时是一个分区的 leader,另一个分区的 follower。 这样可以平衡负载,避免所有的请求都只让一台或者某几台服务器处理。
  • message:

    • 消息,或称日志消息,是 Kafka 服务端实际存储的数据,每一条消息都由一个 key、一个 value 以及消息时间戳 timestamp 组成
  • offset:

    • 偏移量,分区中的消息位置,由 Kafka 自身维护,Consumer 消费时也要保存一份 offset 以维护消费过的消息位置。

1.3、四个核心 API

  • Producer API 发布消息到1个或多个 topic(主题)中。
  • Consumer API 来订阅一个或多个topic,并处理产生的消息。
  • Streams API 充当一个流处理器,从1个或多个 topic 消费输入流,并生产一个输出流到1个或多个输出 topic,有效地将输入流转换到输出流。
  • Connector API 可构建或运行可重用的生产者或消费者,将 topic 连接到现有的应用程序或数据系统。例如,连接到关系数据库的连接器可以捕获表的每个变更。

二、kafka 客户端

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>${kafka.version}</version>
</dependency>

2.1、 KafkaProduce

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;

import java.util.Properties;

/**
 * @author xiandongxie  2020-06-04
 */
public class KafkaProducerTest {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "nn1.hadoop:9092,nn2.hadoop:9092,s1.hadoop:9092");
        // 判别请求是否为完整的条件(判断是不是成功发送了)。指定了“all”将会阻塞消息,这种设置性能最低,但是是最可靠的
        props.put("acks", "all");
        // 如果请求失败,生产者会自动重试,我们指定是0次,如果启用重试,则会有重复消息的可能性
        props.put("retries", 0);
        // 生产者缓存每个分区未发送的消息。缓存的大小是通过 batch.size 配置指定的。值较大的话将会产生更大的批。并需要更多的内存(每个“活跃”的分区都有1个缓冲区)
        props.put("batch.size", 16384);
        // 默认缓冲可立即发送,即便缓冲空间还没有满,但是,如果想减少请求的数量,可以设置 linger.ms 大于0。
        // 这将指示生产者发送请求之前等待一段时间,希望更多的消息填补到未满的批中。这类似于TCP的算法,例如,可能100条消息在一个请求发送,因为我们设置了linger(逗留)时间为1毫秒,然后,如果我们没有填满缓冲区,这个设置将增加1毫秒的延迟请求以等待更多的消息。需要注意的是,在高负载下,相近的时间一般也会组成批,即使是 linger.ms=0。在不处于高负载的情况下,如果设置比0大,以少量的延迟代价换取更少的,更有效的请求。
        props.put("linger.ms", 1);
        // 控制生产者可用的缓存总量,如果消息发送速度比其传输到服务器的快,将会耗尽这个缓存空间。当缓存空间耗尽,其他发送调用将被阻塞,阻塞时间的阈值通过 max.block.ms 设定,之后它将抛出一个TimeoutException。
        props.put("buffer.memory", 33554432);
        // key.serializer 和 value.serializer,将用户提供的 key 和 value 对象 ProducerRecord 转换成字节,可以使用附带的ByteArraySerializaer或StringSerializer处理简单的string或byte类型。
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        Producer<String, String> producer = new KafkaProducer<>(props);
        for (int i = 100; i < 500; i++)
            // send()方法是异步的,添加消息到缓冲区等待发送,并立即返回。生产者将单个的消息批量在一起发送来提高效率
            producer.send(new ProducerRecord<String, String>("my-topic", Integer.toString(i), Integer.toString(i)));

        producer.close();

    }
}

2.2、KafkaConsumer

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.util.Arrays;
import java.util.Properties;

/**
 * 【自动提交偏移量】的简单的kafka消费者API
 *
 * @author xiandongxie
 */
public class KafkaConsumerTest {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "nn1.hadoop:9092,nn2.hadoop:9092,s1.hadoop:9092");
        // 消费者组名称
        props.put("group.id", "test");
        // 设置 enable.auto.commit,偏移量由 auto.commit.interval.ms 控制自动提交的频率。
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        // 指定订阅 topic 名称
        consumer.subscribe(Arrays.asList("my-topic"));
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records)
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
        }

    }
}
posted @ 2020-06-04 18:34  abc十号  阅读(6449)  评论(0编辑  收藏  举报