kafka框架

kafka

整体框架思维导图请访问:https://download.csdn.net/download/qq_38705144/14057057

kafka概述

消息队列的两种模式

1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
2)发布/订阅模式(一对多,消费者消费数据之后不会清除消息)
在这里插入图片描述
在这里插入图片描述

  • (1)Producer :消息生产者,就是向kafka broker发消息的客户端;
  • (2)Consumer :消息消费者,向kafka broker取消息的客户端;
  • (3)Consumer Group (CG):消费者组,由多个consumer组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
  • (4)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
  • (5)Topic :可以理解为一个队列,生产者和消费者面向的都是一个topic;
  • (6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列;
  • (7)Replica:副本,为保证集群中的某个节点发生故障时,该节点上的partition数据不丢失,且kafka仍然能够继续工作,kafka提供了副本机制,一个topic的每个分区都有若干个副本,一个leader和若干个follower。
  • (8)leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是leader。
  • (9)follower:每个分区多个副本中的“从”,实时从leader中同步数据,保持和leader数据的同步。leader发生故障时,某个follower会成为新的leader。

基础架构

  • producer

  • consumer

  • consumer group

  • broker

    • 一台kafka服务器就是一个broker,一个broker容纳多个topic
  • topic

    • 队列,连接生产者和消费者
  • partition

    • topic分为多个partition,实现分布式
  • replica

    • 副本
  • leader

    • 多个副本中的leader
  • follower

    • 从副本,leader故障选举新的follower

安装部署

配置 config/ server.properties

  • broker.id
  • log.dirs
  • zookeeper.connect

启动集群脚本

#! /bin/bash
#1、判断参数是否传入
#$@ - 把所有的输入参数当做个体 a.sh hello spark scala
#$# - 获取参数的个数
#$* - 把所有的输入参数当做整体
#报错:/bin/bash^M: 坏的解释器: 没有那个文件或目录
#运行命令 sed -i 's/\r$//' kafkaCluster
if [ $# -lt 1 ]
then
	echo "必须输入一个参数...."
	exit
fi
#2、根据传入的参数匹配逻辑
case $1 in
"start")
	for host in hadoop102 hadoop103 hadoop104
	do
		echo "=================start $host kafka=============="
		ssh $host "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
	done
;;
"stop")
	for host in hadoop102 hadoop103 hadoop104
	do
		echo "=================stop $host kafka=============="
		ssh $host "/opt/module/kafka/bin/kafka-server-stop.sh"
	done
;;
"status")
	for host in hadoop102 hadoop103 hadoop104
	do
		pid=$(ssh $host "ps -ef|grep kafka |grep -v grep")
		[ "$pid" ] && echo "$host kafka进程正常" || echo "$host kafka进程不存在或者异常"
	done
;;
*)
	echo "参数输入错误....."
;;
esac

常用指令

启动、停止

  • 启动

    kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties

    -daemon:实现后台运行

    /opt/module/kafka/config/server.properties 启动所需的配置文件

    • kafka-server-start.sh
  • 停止

    kafka-server-stop.sh

    • kafka-server-stop.sh

topic指令

  • 创建topic

    kafka-topics.sh --create --bootstrap-server hadoop102:9092,hadoop103:9092 --topic first --partitions 2 --replication-factor 3

    –bootstrap-server:使用的集群(可以设置一个,当102宕机也可以访问)
    –partitions:设置分区数
    -replication-factor:副本数(默认1)
    –topic 定义topic名

    • –create

    • –bootstrap-server

      • +集群名
    • –partitions

      • +分区数
    • –replication-factor

      • +副本数
    • –topic

      • +topic名字
  • 删除topic

    kafka-topics.sh --delete --bootstrap-server hadoop102:9092,hadoop103:9092 --topic topic名

    • –delete

    • –bootstrap-server

      • +集群名
    • –topic

      • +topic名字
  • 查看所有topic

    kafka-topics.sh --list --bootstrap-server hadoop102:9092,hadoop103:9092

    –list :查看所有topic

    • –list
  • 查看topic详细信息

    • –describe
  • 修改信息

    • –alter --partitions

      • 只能修改partitions,且partitions的值只能增大

生产者消费者指令

  • 生产者

    • kafka-console-produce

    • –topic

      • +名字
    • –broker-list

      • +集群名(hadoop102:9092,hadoop103:9092)
  • 消费者

    • kafka-console-consumer

    • –topic

      • +名字
    • –bootstrap-server

      • +集群名(hadoop102:9092,hadoop103:9092)
    • 消费者拉取数据默认是从消费者启动之后开始拉取数据

    • 若拉取所有数据

      • –from-beginning

        • 拉取的数据是无序的
    • 每次启动都是一个新的consumer

    • 若创建新的消费者组,用创建的消费者组拉取数据,则不会拉取之前数据

      • 创建/使用消费者组 --group +组名

查看logs信息

  • kafka-dump-log.sh

    • -file + 查看的文件

      • –print-data-log //输出到控制台在这里插入图片描述

架构

在这里插入图片描述

文件存储机制

在这里插入图片描述

  • 新启动的消费者拉取数据是从最后 的offset+1的位置开始
    • 流程

      • 1、通过segment名确定在offset哪个segment
      • 2、通过index确定offset所在区间
      • 3、扫描对应的log文件确定offset的位置
    • segment

      • 第一个segment名:0000
        第n个segment名:(n-1)的segment中offset+1
      • 好处:可以通过segment名快速确定查找的offset所在segment
      • 默认segment的大小是1G
    • index

      • 将文件每隔一定区间建一个索引

        • 官网index默认值:4k

        • offset:标识,满4k得到一个offset

        • position:物理存储地址

    • log

      • 存储文件内容
    • timeindex

      • log存储文件的时间索引

生产者

  • 分区策略

    • 为什么分区?

      • 1、提高并发量
      • 2、可在集群扩展
    • 原则

      • 情况1:直接指定分区号

      • 情况2:kv键值对,通过key的hashcode值%partitions的个数=分区号

      • 情况3:默认

        • 新版,黏性分区

          • 1、先生成一随机数,随机数%分区数=分区号
          • 2、先排除上次分区,从剩余分区随机选择发送
        • 旧版,轮询机制(合理)

          • 1、先生成一随机数,随机数%分区数=分区号
          • 2、第n次:随机数+(n-1)%分区数=分区号
    • 数据可靠性

      • 1、副本数据同步

        • 副本全部完成同步,才发送ack

          • 延迟高
      • ISR

        • 和leader保持同步的follower集合

          • LEO:当前分区的最大offset
          • HW:该tpic中最小的offset
          • follower故障之后,若该follower的LEO>=HW,则重新加入ISR
      • ack机制

        • 概述:topic的每个partition收到producer发送的数据后,都需要向producer发送ack

        • ack=0:不接受应答,不断发数据

          • 数据丢失
        • ack=1:leader接收到数据发送ack

          • 当follower没有同步数据leader就故障,数据丢失
        • ack=-1:leader接受数据,follower同步完ack

          • 数据重复
      • 故障细节处理

        • follow故障

          • 1、将故障的follow踢出ISR
          • 2、若follow恢复,先读取本地磁盘中HW
          • 3,将log文件中高于HW的数据删除,重新开始同步leader中的数据
          • 4、follower的LEO大于等于该Partition的HW,加入ISR
        • leader故障

          • 1、新选举一个leader

          • 2、新leader将大于HW的数据重新接收

            • 会导致数据重复
          • 3、其他follower先删除大于HW的数据,再同步leader中的数据

          在这里插入图片描述
          在这里插入图片描述

      • exactly once

        • 幂等性(0.11版本引入)

          • Producer不论向Server发送多少次重复数据,Server端都只会持久化一条

          • 开启方式:enable.idompotence

          • 原理

            • 主键约束
              producerID+partitionID+SequenceNUM

              producerID:生产者ID
              partitionID:分区号
              sequenceNUM:队列数据位置

            • 要保证produce不变,否则会导致数据重复

消费者

  • 消费方式

    • push(推送方式)

      • 很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的
    • pull(拉取方式)

  • 分区分配策略

    • range(轮询)

    • roundrobin

      • 1、确定每个消费者大概消费几个分区的数据,分区数/消费者个数
      • 2、 确定前多少个消费者多消费一个分区的个数
        分区数%消费者个数=前n个消费者

kafka API

消息发送流程

  • 流程

    • 1、producer将消息封装为producerRecoder

    • 2、消息传递给拦截器(数据筛选、清洗)

    • 3、序列化器,进行数据传输

    • 4、分区器确定数据发送至哪个分区(数据以批次的方式进行打包)

    • 5、数据进入共享变量recordAccumulator

    • 6、send线程拉取数据发送到broker

      • 异步发送

      • 同步发送

        • producer.send(ps, new Callback() {…}).get //通过.get方法获取数据,相当于阻塞
  • 程序流程

  import org.apache.kafka.clients.producer.*;

  import java.nio.channels.AsynchronousByteChannel;
  import java.util.Properties;
  import java.util.concurrent.ExecutionException;

  /**

   * @ClassName : MyProducer
   * @Author : kele
   * @Date: 2021/1/8 14:10
   * @Description :自定义生产者
   * 1、创建一个生产者:producer
   * 2、创建配置文件properties,包含生产者相关信息,
   * key,value序列化方式
   * ack的模式
   * 运行的集群
   * 3、将数据封装为ProducerRecord,
     */
     public class MyProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

      //异步发送,效率高
         //AsySend();
      //同步发送数据
         SynSend();
    }

    /**

     * 异步发送数据
       */

    public static void AsySend(){

      /**

     * 2、设置配置信息
       /
           Properties props = new Properties();
           props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");  //设置发送数据的key序列化方式
           props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer"); //设置发送数据的value序列化方式
           props.put("acks","all");   //设置ack,可以为-1(all),0,1
           props.put("bootstrap.servers","hadoop102:9092,hadoop103:9092");  //设置运行的集群
           props.put("enable.idempotence","true");   //设置是否使用幂等性
           /**
          * 1、创建生产者,构造函数需要properties的配置信息
            /
                Producer<String,String> producer = new KafkaProducer<String, String>(props);

      for (int i = 11; i <= 15; i++) {

        /**

     * 3、将数据封装为producerRecord的形式,

             ProducerRecord<String, String> ps = new ProducerRecord<String, String>("first","send "+i+" message");
             //4、发送数据
             producer.send(ps);
           }
           producer.close();
         }

    /**

     * 同步发送数据
     * producer.send(ps, new Callback() {...}).get  //通过.get方法获取数据,相当于阻塞
       */
         public static void SynSend() throws ExecutionException, InterruptedException {
           /**
          * 2、设置配置信息
            /
                Properties props = new Properties();
                props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");  //设置发送数据的key序列化方式
                props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer"); //设置发送数据的value序列化方式
                props.put("acks","all");   //设置ack,可以为-1(all),0,1
                props.put("bootstrap.servers","hadoop102:9092,hadoop103:9092");  //设置运行的集群
                props.put("enable.idempotence","true");   //设置是否使用幂等性
                /**
               * 1、创建生产者,构造函数需要properties的配置信息
                 /
                     Producer<String,String> producer = new KafkaProducer<String, String>(props);

      for (int i = 11; i <= 15; i++) {

        /**

     * 3、将数据封装为producerRecord的形式,

             ProducerRecord<String, String> ps = new ProducerRecord<String, String>("first","send "+i+" message");
             //4、发送数据,并且接受返回相关数据
             System.out.println("send num: " + i);
             producer.send(ps, new Callback() {
               @Override
               public void onCompletion(RecordMetadata recordMetadata, Exception e) {
                 //获取返回的offset值
                 long offset = recordMetadata.offset();
                 //获取返回的分区信息
                 int partition = recordMetadata.partition();
                 //获取topic
                 String topic = recordMetadata.topic();

            System.out.println("receive: topic-->"+topic+",partition-->"+partition+",offset-->"+offset);
          }
        }).get();
      }
      producer.close();
    }
  }

生产者流程

  • 1、创建生产者对象(构造函数包含配置properties)

  • 2、配置properties

    • key、value的序列化方式
    • ack的模式
    • 运行的集群
  • 3、数据封装成producerRecoder(topic,value)

消费者流程


  /**

   * @ClassName : MyCustomerTest

   * @Author : kele

   * @Date: 2021/1/8 19:56

   * @Description :自定义customer

   * 1、创建一个kafkaconsumer,构造方法需要参数类properties

   * 1)、key,value序列化方式

   * 2)、使用集群

   * 3)、设置开始读取位置

   * 4)、是否自动提交事务

   * 2、确定consumer订阅的消息topic(集合的形式),myconsumer.subscribe(mycluster);

   * 3、使用poll方法拉取数据(构造方法中设置时间间隔)
     */
     public class MyCustomerTest {

     public static void main(String[] args) {

         Properties pro = new Properties();
         
         pro.setProperty("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
         pro.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
         pro.setProperty("bootstrap.servers","hadoop102:9092,hadoop103:9092");
         pro.setProperty("group.id","g2");
         //配置开始读取数据的位置
         pro.setProperty("auto.offset.reset","earliest");
         //是否自动提交
         pro.setProperty("enable.auto.commit", "false");

     //        1、创建一个kafkaconsumer,构造方法需要参数类properties
         KafkaConsumer<String, String> ms = new KafkaConsumer<>(pro);

         while(true){
             //确定从哪个topic获取数据
             List<String> mycluster = new ArrayList<String>();
             mycluster.add("first");

     //            2、确定consumer订阅的消息topic(集合的形式),myconsumer.subscribe(mycluster);
             ms.subscribe(mycluster);
     //            3、使用poll方法拉取数据(构造方法中设置时间间隔)
             ConsumerRecords<String, String> poll = ms.poll(Duration.ofSeconds(5));

             Iterator<ConsumerRecord<String, String>> iterator = poll.iterator();
         
             while(iterator.hasNext()){
                 ConsumerRecord<String, String> next = iterator.next();
                 String topic = next.topic();
                 String key = next.key();
                 String value = next.value();
                 long offset = next.offset();
         
                 System.out.println("receive: topic-->"+topic+",key-->"+key+",value-->"+value+",offset-->"+offset);
         
                 //异步提交
                 ms.commitAsync(new OffsetCommitCallback() {
                     @Override
                     public void onComplete(Map<TopicPartition, OffsetAndMetadata> map, Exception e) {
                         Iterator<Map.Entry<TopicPartition, OffsetAndMetadata>> iterator1 = map.entrySet().iterator();
         
                         while(iterator1.hasNext()){
                             Map.Entry<TopicPartition, OffsetAndMetadata> next1 = iterator1.next();
                             int partition = next1.getKey().partition();
                             String topic1 = next1.getKey().topic();
                             long offset1 = next1.getValue().offset();
         
                             System.out.println("partition-->"+partition+",topic-->"+topic1+",offset-->"+offset1);
                         }
                     }
                 });
             }
         }

     }
     }

  - 1、创建一个kafkaconsumer,构造方法需要参数类properties

    - 1)、key,value序列化方式
    - 2)、使用集群
    - 3)、设置开始读取位置
    - 4)、是否自动提交事务

  - 2、确定consumer订阅的消息topic(集合的形式),myconsumer.subscribe(mycluster);
  - 3、使用poll方法拉取数据(构造方法中设置时间间隔)

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值