• Kafka速览


    一、基本结构

    三台机器组成的Kafka集群,每台机器启动一个Kafka进程,即Broker

    向broker发送消息的客户端是Producer,拉取消息的客户端是Consumer

    Producer和Consumer都是用户实现的

    broker只负责数据存储,不保存任何Producer和Consumer的信息

    Kfaka通过zookeeper管理集群

    broker和集群的一些信息,即元信息都保存在zookeeper中

    一个ConsumerGroup可以包含一个或多个Consumer

    同一个消费者组里的不同消费者是互斥的,在一个消费者组里,一条消息运行被一个Consumer消费,不会出现同一条消息被一个消费者组多次消费的情况

    Topic与Partition

    Kafka集群由多个实例组成,每个节点称为Broker,对消息保存时根据Topic进行归类

    一个Topoic可以被划分为多个Partition

    每个Partition可以有多个副本,多副本冗余存储可以保证某个副本的机器出现故障,其他副本可正常使用,副本尽量分布到不同的broker上,分区的每个副本都有一个leader负责读写请求,其他副本只从leader中同步数据,leader挂掉会从follower中选举新的leader

     一个topic可看成数据库中的一个表

    消费者可以并行处理Partition中的数据

    Partition内顺序存储,写入新消息采用追加的方式,消费消息采用FIFO的方式顺序拉取消息

    一个Topic可以有多个分区,Kafka只保证同一个分区内有序,不保证Topic整体(多个分区之间)有序

    ConsumerGroup

     CG,为了加速读取速度,多个consumer可以划分为一个组并行消费一个Topic

    一个Topic可以由多个CG订阅,多个CG之间是平等的,同一个CG内可以有一个或多个consumer,同一个CG内的consumer之间是竞争关系,一个消息在一个CG内的只能被一个consumer消费

    核心概念总结:

    • Broker:启动Kafka的一个实例就是一个broker,一个kafka集群可以启动多个broker
    • Topic:相当于数据库中的表,productor将消息写入的一个topic中,consumer从同一个topic消费消息
    • Partiton:一个topic可以设置多个分区,相当于把一个数据集分成多份分别放到不同的分区中存储,一个topic可以有一个或者多个分区,分区内消息有序
    • Replication:副本,一个Partition可以设置一个或多个副本,副本主要保证系统能够持续不丢失的对外提供服务,提高系统的容错能力
    • Producer:消费生产者,负责向Kafka中发布消息
    • Consumer Group:消费者所属组,一个Consumer Group可以包含一个或多个consumer,当一个topic被一个Consumer Group消费的时候,Consumer Group内只能有一个consumer消费同一条消息,不会出现同一个Consumer Group多个consumer同时消费一条消息造成一个消息被一个Consumer Group消费多次的情况
    • Consumer:消息消费者,consumer从kafka指定的主题中拉取消息
    • Zookeeper:Zookeeper在Kafka集群中主要用于协调管理,Kafka将元数据信息保存在zookeeper中,通过zookeeper管理和维护整个Kafka集群的动态扩展、各个Broker负载均衡、Partition leader选举等

    二、Kafka存储

    每个topic可以有多个分区,每个分区在物理磁盘上就是一个文件夹(目录)

    蓝色前本部分是所属topic的名称,后面的数字是分区的编号

     在每个分区里有多个segment文件

    Segment:段文件,Kafka中最小存储单位,一个partition包含多个segment文件,每个segment是以message在partition中的起始偏移量命名以log结尾的文件

    Offset:消息在分区中的偏移量,用来在分区中唯一的标识一条信息

    索引文件

    Kafka为了提高写入、查询速度,在partition文件夹下每一个segment log文件都有同名的索引文件,在Kafka0.10以后的版本中会存在两个索引文件

    • 一个是以index结尾的偏移量索引文件
    • 另一个是以timeinde结尾的时间戳索引文件

    偏移量索引文件

    以偏移量作为名称,index为后缀

    索引内容格式:offset,position

    采用稀疏存储方式

    通过log.index.interval.bytes设置索引跨度

     比如查找offset4的消息

    consumer首先确定在哪个数据文件和索引文件中

    通过偏移量索引文件查找小于等于指定偏移量最大的偏移量,上面再索引文件中找到offset3

    根据offset3指向的数据文件里面对应的地方往下找,找到offset4

    时间戳索引文件

    以时间戳为名称,以timeindex为后缀

    索引内容格式:timestamp,offset

    采用稀疏存储方式

    通过log.index.interval.tytes设置索引跨度

     查找过程同偏移量索引

    三、高可用的实现

    多分区多副本

    • 一个topic可以有多个分区,每个分区可以有多个副本
    • 0.8以前没有Replication,一旦某台broker宕机,其上partition数据便丢失
    • 同一个partition的不同副本分不到不同的broker
    • 一个分区的多个副本选举一个leader,由leader负责读写,其他副本作为follower从leader同步消息

    Kafka Controller选举

    • 从集群中的broker选举出一个Broker作为Controller控制节点
    • 负责整个集群的管理,如Broker管理、Topic管理、Partition Leader选举等
    • 选举过程所有的Broker向Zookeeper发起创建临时znode的请求,成功创建znode的Broker胜出作为Controller,未被选中的Broker监听Controller的znode,等待下次选举

    Kafka Partition Leader选举

    • Controller负责分区Leader选举
    • ISR列表 在zookeeper中

                       Follower批量从Leader拖取数据

                       Leader跟踪保持同步的flower列表ISR(In Sync Replica),ISR作为下次选主的候选列表

                       Follower心跳超时或者消息落后太多,将被移除出ISR

    • Leader失败后,从ISR列表中选择一个Follower作为新的Leader

    四、Kafka集群部属

    1.集群规划
        使用3台机器部署,分别是node01、node02、node03
    2.下载Kafka安装包
        下载地址http://kafka.apache.org/downloads,选择Kafka版本kafka_2.11-0.10.2.1.tgz
    3.安装kafka
        将安装包上传到其中一台机器node01上,并解压到/bigdata目录下:tar -zxvf kafka_2.11-0.10.2.1.tgz
        创建软连接:ln -s /bigdata/kafka_2.11-0.10.2.1 /usr/local/kafka
    4.添加到环境变量:vim /etc/profile
        添加内容:export KAFKA_HOME=/usr/local/kafka
                export PATH=$PATH:${KAFKA_HOME}/bin
        刷新环境变量:source /etc/profile
    5.修改配置文件
        cd /usr/local/kafka/config
        vim server.properties
    6.在/usr/local/kafka中创建kafka-logs文件夹
        mkdir /usr/local/kafka/kafka-logs
    7.使用scp将配置好的kafka安装包拷贝到node02和node03两个节点
        scp -r /bigdata/kafka_2.11-0.10.2.1 root@node02:/bigdata/
        scp -r /bigdata/kafka_2.11-0.10.2.1 root@node03:/bigdata/
    8.分别修改node02和node03的配置文件server.properties 具体文件在下面
        8.1 node02的server.properties修改项
        broker.id=1
        host.name=node02
        8.2 node03的server.properties修改项
        broker.id=2
        host.name=node03
    9.分别在node01、node02、node03启动kafka
        cd /usr/local/kafka
        启动的时候使用-daemon选项,则kafka将以守护进程的方式启动
        bin/kafka-server-start.sh -daemon config/server.properties
    10.日志目录
        默认在kafka安装路径生成的logs文件夹中

    server.properties

    ############################# Server Basics #############################
    
    #每个borker的id是唯一的,多个broker要设置不同的id
    broker.id=0
    
    #访问端口号
    port=9092
    
    #访问地址
    host.name=node01
    
    #允许删除topic
    delete.topic.enable=true
    
    
    # The number of threads handling network requests
    num.network.threads=3
    
    # The number of threads doing disk I/O
    num.io.threads=8
    
    # The send buffer (SO_SNDBUF) used by the socket server
    socket.send.buffer.bytes=102400
    
    # The receive buffer (SO_RCVBUF) used by the socket server
    socket.receive.buffer.bytes=102400
    
    # The maximum size of a request that the socket server will accept (protection against OOM)
    socket.request.max.bytes=104857600
    
    
    ############################# Log Basics #############################
    
    #存储数据路径,默认是在/tmp目录下,需要修改
    log.dirs=/usr/local/kafka/kafka-logs
    
    #创建topic默认分区数
    num.partitions=1
    
    # The number of threads per data directory to be used for log recovery at startup and flushing at shutdown.
    # This value is recommended to be increased for installations with data dirs located in RAID array.
    num.recovery.threads.per.data.dir=1
    
    ############################# Log Flush Policy #############################
    
    # Messages are immediately written to the filesystem but by default we only fsync() to sync
    # the OS cache lazily. The following configurations control the flush of data to disk.
    # There are a few important trade-offs here:
    #    1. Durability: Unflushed data may be lost if you are not using replication.
    #    2. Latency: Very large flush intervals may lead to latency spikes when the flush does occur as there will be a lot of data to flush.
    #    3. Throughput: The flush is generally the most expensive operation, and a small flush interval may lead to exceessive seeks.
    # The settings below allow one to configure the flush policy to flush data after a period of time or
    # every N messages (or both). This can be done globally and overridden on a per-topic basis.
    
    # The number of messages to accept before forcing a flush of data to disk
    #log.flush.interval.messages=10000
    
    # The maximum amount of time a message can sit in a log before we force a flush
    #log.flush.interval.ms=1000
    
    ############################# Log Retention Policy #############################
    
    # The following configurations control the disposal of log segments. The policy can
    # be set to delete segments after a period of time, or after a given size has accumulated.
    # A segment will be deleted whenever *either* of these criteria are met. Deletion always happens
    # from the end of the log.
    
    #数据保存时间,默认7天,单位小时
    log.retention.hours=168
    
    # A size-based retention policy for logs. Segments are pruned from the log as long as the remaining
    # segments don't drop below log.retention.bytes. Functions independently of log.retention.hours.
    #log.retention.bytes=1073741824
    
    # The maximum size of a log segment file. When this size is reached a new log segment will be created.
    log.segment.bytes=1073741824
    
    # The interval at which log segments are checked to see if they can be deleted according
    # to the retention policies
    log.retention.check.interval.ms=300000
    
    ############################# Zookeeper #############################
    
    #zookeeper地址,多个地址用逗号隔开
    zookeeper.connect=node01:2181,node02:2181,node03:2181
    
    # Timeout in ms for connecting to zookeeper
    zookeeper.connection.timeout.ms=6000

    五、主题管理


    创建主题
    bin/kafka-topics.sh --create --zookeeper node01:2181 --topic topic1 --replication-factor 2 --partitions 2

    查看主题信息
    bin/kafka-topics.sh --describe --zookeeper node01:2181 --topic topic1

    查看kafka中已经创建的主题列表
    bin/kafka-topics.sh --list --zookeeper node01:2181


    使用kafka自带的生产者客户端脚本
    bin/kafka-console-producer.sh --broker-list node01:9092,node02:9092,node03:9092 --topic topic1

    使用kafka自带的消费者客户端脚本
    bin/kafka-console-consumer.sh --zookeeper node01:2181 --from-beginning --topic topic1


    删除topic:
    bin/kafka-topics.sh --delete --zookeeper node01:2181 --topic topic1

    增加分区
    bin/kafka-topics.sh --alter --zookeeper node01:2181 --topic topic1 --partitions 3

    六、Java API

    Producer

    关键:

    • 配置
    • 新建Producer
    • 发送消息
    public class Producer extends Thread
    {
      private final kafka.javaapi.producer.Producer<Integer, String> producer;
      private final String topic;
      private final Properties props = new Properties();
    
      public Producer(String topic)
      {
        props.put("serializer.class", "kafka.serializer.StringEncoder"); // 定义message的类型为字符串
       props.put("metadata.broker.list", KafkaProperties.broker_list);
        // Use random partitioner. Don't need the key type. Just set it to Integer.
        // The message is of type String.
        producer = new kafka.javaapi.producer.Producer<Integer, String>(new ProducerConfig(props));
        this.topic = topic;
      }
      
      public void run() {
        int messageNo = 1;
        while(true)
        {
          String messageStr = new String("Message_" + messageNo);
          producer.send(new KeyedMessage<Integer, String>(topic, messageStr));
          System.out.println("已生成数据: " + messageStr);
          messageNo++;
        }
      }
    
      public static void main(String[] args) {
        Producer producerThread = new Producer(KafkaProperties.TestTopic);
        producerThread.start();
      }
    }

    Consumer

    public class Consumer extends Thread
    {
      private final ConsumerConnector consumer;
      private final String topic;
      
      public Consumer(String topic)
      {
        consumer = kafka.consumer.Consumer.createJavaConsumerConnector(
                createConsumerConfig());
        this.topic = topic;
      }
    
      private static ConsumerConfig createConsumerConfig()
      {
        Properties props = new Properties();
        props.put("zookeeper.connect", KafkaProperties.zkConnect);
        props.put("group.id", KafkaProperties.groupId);
        props.put("zookeeper.session.timeout.ms", "400");
        props.put("zookeeper.sync.time.ms", "200");
        props.put("auto.commit.interval.ms", "1000"); // 多久更新一次offset
    
        return new ConsumerConfig(props);
    
      }
     
      public void run() {
        Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
        topicCountMap.put(topic, new Integer(1));
        Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumer.createMessageStreams(topicCountMap);
       KafkaStream<byte[], byte[]> stream =  consumerMap.get(topic).get(0);
        ConsumerIterator<byte[], byte[]> it = stream.iterator();
        while(it.hasNext())
          System.out.println(new String(it.next().message()));
      }
    
      public static void main(String[] args) {
        Consumer consumerThread = new Consumer(KafkaProperties.topic);
        consumerThread.start();
      }
    }
  • 相关阅读:
    前端TypeScript编写的代码发布后怎么在浏览器中调试
    oracle中的执行计划
    oracle中的物化视图
    oracle中的exists 和not exists 用法
    Oracle中的索引详解
    Oracle中动态SQL拼接
    oracle 中sql优化的几种方法
    oracle中常用函数大全
    Oracle中游标的用法
    oracle中表分区的实现
  • 原文地址:https://www.cnblogs.com/aidata/p/11563180.html
Copyright © 2020-2023  润新知