Kafka笔记

Kafka

Kafka 中文文档 - ApacheCN

学习 Kafka 入门知识看这一篇就够了!(万字长文) - 腾讯云开发者社区-腾讯云 (tencent.com)

简介

分布式、多分区、多副本,基于Zookeeper的分布式消息流平台,基于发布订阅模式的消息引擎系统

基本术语

  1. 消息:kafka中的数据单元被称为消息,也称为记录, 类似数据库表中的某一行的记录
  2. 批次:一组消息就是批次
  3. 主题(topic):消息的种类称为主题(Topic),一个主题代表一类消息
  4. 分区(partition):主题可以被分为若干个分区,同一个主题的分区可以不在一个机器上,有可能部署在多个机器上,由此实现kafka的伸缩性
    1. 单一主题中的分区有序,但无法保证主题中所有的分区有序
  5. 段(segment):将分区进一步细分为若干个segment,每个segment文件的大小相等
  6. 生产者:向主题发布消息的客户端应用程序称为生产者(Producer)
  7. 消费者:订阅主题消息的客户端程序称为消费者(Consumer)
  8. 消费者群组:由一个或多个消费者组成的群体
  9. 偏移量:一种元数据,一个不断递增的整数值,用来记录消费者发送重平衡时的位置,以便来恢复数据
  10. broker:一个独立的Kafka服务器就称为broker,broker接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存
  11. broker集群:由一个或多个broker组成,每个集群都有一个broker同时充当了 集群控制器 的角色
  12. 副本:消息的备份又称为副本,副本的数量是可以配置的。Kafka定义了两类副本:
    1. 领导者副本(Leader Replica),对外提供服务
    2. 追随者副本(Follower Replica),被动追随
  13. 重平衡(Rebalance):消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。是Kafka消费者段实现高可用的重要手段

特性

  1. 持久性、可靠性 、低延迟
  2. 高吞吐、高伸缩性、容错性、高并发

核心API

  1. Producer API:允许应用程序向一个或多个topics上发送消息
  2. Consumer API:允许应用程序订阅一个或多个topics并处理为其生成的记录流
  3. Streams API:允许应用程序作为流处理器,从一个或多个主题中消费输入流并为其生成输出流,有效的将输入流转换为输出流
  4. Connector API:允许构建和允许将Kafka主题链接到现有应用程序或数据系统的可用生产者和消费者

为何如此之快

Kafka 实现了零拷贝原理来快速移动数据,避免了内核之间的切换。Kafka 可以将数据记录分批发送,从生产者到文件系统(Kafka 主题日志)到消费者,可以端到端的查看这些批次的数据

  • 顺序读写
  • 零拷贝
  • 消息压缩
  • 分批发送

Kafaka的消息队列

点对点模式

在这里插入图片描述

发布-订阅模式

在这里插入图片描述

重要配置

broker.id

每个kafka broker 都有一个唯一的标识来表示,这个唯一的标识符即是 broker.id,它的默认值是 0。这个值在 kafka 集群中必须是唯一的,这个值可以任意设定

port

如果使用配置样本来启动 kafka,它会监听 9092 端口。修改 port 配置参数可以把它设置成任意的端口。要注意,如果使用 1024 以下的端口,需要使用 root 权限启动 kakfa

zookeeper.conect

用于保存 broker 元数据的 Zookeeper 地址是通过 zookeeper.connect 来指定的

auto.create.topics.enable

默认情况下,kafka 会使用三种方式来自动创建主题,下面是三种情况:

当一个生产者开始往主题写入消息时

当一个消费者开始从主题读取消息时

当任意一个客户端向主题发送元数据请求时

主题默认配置

num.partitions

指定了新创建的主题需要包含多少个分区

default.replication.factor

表示 kafka保存消息的副本数,如果一个副本失效了,另一个还可以继续提供服务default.replication.factor 的默认值为1,这个参数在你启用了主题自动创建功能后有效

log.retention.ms

Kafka 通常根据时间来决定数据可以保留多久

message.max.bytes

参数来限制单个消息的大小,默认是 1000 000, 也就是 1MB

retention.ms

规定了该主题消息被保存的时常,默认是7天,即该主题只能保存7天的消息,一旦设置了这个值,它会覆盖掉 Broker 端的全局参数值

Windows安装

Apache Kafka

Kafka包名组成: Scala版本 - Kafka自身版本

在这里插入图片描述

启动

2.2及以上版本 不再需要 ZooKeeper 连接字符串,即- -zookeeper localhost:2181。使用 Kafka Broker的 --bootstrap-server localhost:9092来替代- -zookeeper localhost:2181

  • bin/windows 目录下
  • 修改配置文件中:log.dirs的存储地址
  • 启动zookeeper:zookeeper-server-start.bat …\config\zookeeper.properties
  • 启动kafka:kafka-server-start.bat …\config\server.properties
  • 创建一个测试主题:kafka-topics.bat --bootstrap-server localhost:9092 --create --topic test1-topic
  • 查看主题列表:kafka-topics.bat --list --bootstrap-server localhost:9092
  • 启动生产者:kafka-console-producer.bat --broker-list localhost:9092 --topic test-topic
  • 启动生产者:kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test-topic --from-beginning

Kafka Producer

创建生产者

Kafka生产者有3个必选属性

  1. bootstrap.servers
    1. 该属性指定 broker 的地址清单,地址的格式为 host:port
  2. key.serializer
    1. broker 需要接收到序列化之后的 key/value值,所以生产者发送的消息需要经过序列化之后才传递给 Kafka Broker
    2. 生产者需要知道采用何种方式把 Java 对象转换为字节数组。key.serializer 必须被设置为一个实现了org.apache.kafka.common.serialization.Serializer 接口的类,生产者会使用这个类把键对象序列化为字节数组
    3. key.serializer 是必须要设置的,即使你打算只发送值的内容
  3. value.serializer
    1. 指定的类会将值序列化
private Properties properties = new Properties();
properties.put("bootstrap.servers","broker1:9092,broker2:9092");
properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties = new KafkaProducer<String,String>(properties);
  • 首先创建了一个 Properties 对象
  • 使用 StringSerializer 序列化器序列化 key / value 键值对
  • 在这里我们创建了一个新的生产者对象,并为键值设置了恰当的类型,然后把 Properties 对象传递给他。

Kafka消息发送

简单消息发送
  • 无法知道消息是否发送成功
ProducerRecord<String,String> record = new ProducerRecord<String, String>("CustomerCountry","West","France");
producer.send(record);
----
    public ProducerRecord(String topic, K key, V value) {}
	这个构造函数,需要传递的是 topic主题,key 和 value
同步发送消息
  • 发送并返回结果
ProducerRecord<String,String> record = new ProducerRecord<String, String>("CustomerCountry","West","France");
try{  RecordMetadata recordMetadata = producer.send(record).get();}catch(Exception e){  e.printStackTrace()}
  • 首先调用 send() 方法,然后再调用 get() 方法等待 Kafka 响应。如果服务器返回错误,get() 方法会抛出异常,如果没有发生错误,我们会得到 RecordMetadata 对象,可以用它来查看消息记录
异步发送消息
  • 发送并回调
  • 同步发送消息都有个问题,那就是同一时间只能有一个消息在发送,这会造成许多消息无法直接发送,造成消息滞后,无法发挥效益最大化
  • ProducerRecord (kafka 2.3.0 API) (apache.org)
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>("CustomerCountry", "Huston", "America");        
producer.send(producerRecord,new DemoProducerCallBack());

class DemoProducerCallBack implements Callback {
  public void onCompletion(RecordMetadata metadata, Exception exception) {    
      if(exception != null){      
          exception.printStackTrace();
      }  
  }
}
  • 实现回调需要定义一个实现了org.apache.kafka.clients.producer.Callback的类,这个接口只有一个 onCompletion方法

生产者分区机制

Kafka对于数据的读写是以分区为粒度的,分区可以分布在多个主机(Broker)中,这样每个节点能够实现独立的数据写入和读取,并且能够通过增加新的节点来增加 Kafka 集群的吞吐量,通过分区部署在多个 Broker 来实现负载均衡的效果

springboot整合

依赖

 <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
</dependency>

yml配置

spring:
  kafka:
    # 节点,多个节点逗号分开
    bootstrap-servers: 127.0.0.1:9092
    # 生产者
    producer:
      # 重试次数
      retries: 3
      batch-size: 16384
      buffer-memory: 33554432
      acks: 1
      # 指定消息key和消息体的编解码方式
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    consumer:
      group-id: default-group
      # 是否自动确认
      enable-auto-commit: false
      auto-offset-reset: earliest
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
    listener:
      # 当每一条记录被消费者监听器(ListenerConsumer)处理之后提交
      # RECORD
      # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后提交
      # BATCH
      # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,距离上次提交时间大于TIME时提交
      # TIME
      # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后,被处理record数量大于等于COUNT时提交
      # COUNT
      # TIME | COUNT 有一个条件满足时提交
      # COUNT_TIME
      # 当每一批poll()的数据被消费者监听器(ListenerConsumer)处理之后, 手动调用Acknowledgment.acknowledge()后提交
      # MANUAL
      # 手动调用Acknowledgment.acknowledge()后立即提交,一般使用这种
      # MANUAL_IMMEDIATE
      ack-mode: manual_immediate

config配置

@Configuration
public class KafkaConfig {

    @Bean
    public void init() {
        Properties properties = new Properties();
        properties.put("bootstrap.servers", "127.0.0.1:9092");

        AdminClient admin = AdminClient.create(properties);

        ArrayList<NewTopic> topics = new ArrayList<NewTopic>();
        // 创建主题  参数:主题名称、分区数、副本数
        NewTopic newTopic = new NewTopic(KafkaCons.TOPIC_NAME, 1, (short) 1);
        topics.add(newTopic);

        CreateTopicsResult result = admin.createTopics(topics);

        try {
            result.all().get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

生产者

@RestController
public class ProducerController {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplates;

    @RequestMapping("/send")
    public String send(@RequestParam("msg") String msg) {
        //主题,key,value
        ProducerRecord<String, String> producerRecord = new ProducerRecord<>(KafkaCons.TOPIC_NAME, UuidUtil.generatorUUID(), msg);
        try {
            //.get() 异步发送消息
            kafkaTemplates.send(producerRecord).get();
            // 添加生产者监听器
            kafkaTemplates.setProducerListener(new ProducerCallBack());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return String.format("消息%s发送成功", msg);
    }
}

监听回调

public class ProducerCallBack implements ProducerListener {
    private static final Logger log = LoggerFactory.getLogger(ProducerCallBack.class);

    @Override
    public void onSuccess(ProducerRecord producerRecord, RecordMetadata recordMetadata) {
        log.info("Message send success : " + producerRecord.toString());
    }

    @Override
    public void onError(ProducerRecord producerRecord, Exception exception) {
        log.info("Message send error : " + producerRecord.toString());
    }
}

消费者

@Component
public class KafkaConsumer {

    @KafkaListener(topics = KafkaCons.TOPIC_NAME, groupId = "testTopicGroup1")
    public void listenTestTopicGroup1(ConsumerRecord<String, String> record, Acknowledgment ack) {
        String value = record.value();
        System.out.println("testTopicGroup1 message:" + value);
        System.out.println("testTopicGroup1 record:" + record);
        ack.acknowledge();
    }

    @KafkaListener(topics = KafkaCons.TOPIC_NAME, groupId = "testTopicGroup2")
    public void listenTestTopicGroup2(ConsumerRecord<String, String> record, Acknowledgment ack) {
        String value = record.value();
        System.out.println("testTopicGroup2 message:" + value);
        System.out.println("testTopicGroup2 record:" + record);
        ack.acknowledge();
    }
}
  • 30
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值