• Kafka学习笔记


    写在前面

    Kafka是一个消息队列框架,在大数据生态体系中有着极其重要的作用。同时其在java后端领域也有着很高的地位。今天就来学习一下kafka

    快速入门

    前置配置

    kafka需要zookeeper的支持,因此要先配置好zookeeper,详见这篇博客: ZooKeeper搭建和使用笔记,这里就默认已经配好了zookeeper

    这里选用的是kafka的0.11的版本,如图:

    image-20210117212425870

    可以从官网进行下载。

    配置

    kafka的配置还是很简单的。首先我们解压压缩包:

     tar -zxvf kafka_2.11-0.11.0.0.tgz -C /opt/module/
    

    在kafka目录下创建data文件夹,用来存储kafka的缓存文件

    mkdir data
    

    接着修改配置文件(config/server.properties)

    cd config/
    vim server.properties
    

    接着修改配置文件:

    #broker 的全局唯一编号,不能重复
    broker.id=0
    #删除 topic 功能使能
    delete.topic.enable=true
    #处理网络请求的线程数量
    num.network.threads=3
    #用来处理磁盘 IO 的现成数量
    num.io.threads=8
    #发送套接字的缓冲区大小
    socket.send.buffer.bytes=102400
    #接收套接字的缓冲区大小
    socket.receive.buffer.bytes=102400
    #请求套接字的缓冲区大小
    socket.request.max.bytes=104857600
    #kafka 运行日志存放的路径
    log.dirs=/opt/module/kafka_2.11-0.11.0.0/data
    #topic 在当前 broker 上的分区个数
    num.partitions=1
    #用来恢复和清理 data 下数据的线程数量
    num.recovery.threads.per.data.dir=1
    #segment 文件保留的最长时间,超时将被删除
    log.retention.hours=168
    #配置连接 Zookeeper 集群地址
    zookeeper.connect=hadoop03:2181,hadoop04:2181,hadoop05:2181
    

    这里要注意,重点要配置的项是broker.id,要全局唯一。我们这里配置的是集群,每台机子的id不能相同。再就是zookeeper集群的配置,这里已经配置了本地host,所以可以直接填写主机名,这里建立都配置一下。

    然后如果需要的话,可以配置一下环境变量:

    sudo vi /etc/profile
     #KAFKA_HOME
    export KAFKA_HOME=/opt/module/kafka_2.11-0.11.0.0
    export PATH=$PATH:$KAFKA_HOME/bin
    source /etc/profile
    

    将配置好的kafka包进行分发:

    xsync kafka_2.11-0.11.0.0/
    

    关于这里的分发的脚本,可以去看我的这篇博客,里面写了如何写这个分发脚本。

    然后在其他的机子上修改一下broker.id就好了。

    启动

    要启动的话,十分简单,在kafka目录:

    bin/kafka-server-start.sh config/server.properties
    

    如果配置了环境变量,可以直接:

    kafka-server-start.sh config/server.properties
    

    然后就发现以上是前台启动,如果想后台启动需要加一个参数:

    bin/kafka-server-start.sh -daemon config/server.properties
    

    但每次每个机子都要自己启一遍太麻烦了。我们可以写一个脚本来群起kafka:

    #!/bin/bash
    
    case $1 in
    "start"){
            for i in hadoop03 hadoop04 hadoop05
            do
                    echo "*****************$i****************"
                    ssh $i "/opt/module/kafka_2.11-0.11.0.0/bin/kafka-server-start.sh -daemon /opt/module/kafka_2.11-0.11.0.0/config/server.properties"
            done
    };;
    "stop"){
            for i in hadoop03 hadoop04 hadoop05
            do
                    echo "*****************$i****************"
                    ssh $i "/opt/module/kafka_2.11-0.11.0.0/bin/kafka-server-stop.sh"
            done
    };;
    
    esac
    

    这里要注意修改主机名,同时注意修改各个文件的位置。该脚本要放在当前用户的bin目录下,如我的就放在了:

    image-20210117213714831

    然后给它权限:

    chmod 777 kk.sh
    

    我们就可以全局使用了:

    # 启动
    kk.sh start
    # 停止
    kk.sh stop
    

    命令行使用

    kafka的命令行能做到的事十分少,这里就列举一些比较常用的命令行操作,方便测试。

    topic的增删查

    • 列出当前所有topic:

      bin/kafka-topics.sh --zookeeper hadoop03:2181 --list
      

      这里要指定服务器的地址。

    • 创建topic:

      bin/kafka-topics.sh --zookeeper hadoop03:2181 --create --replication-factor 3 --partitions 1 --topic first
      

      这里要指定分区数,分片数,和topic名字。

    • 删除topic

      bin/kafka-topics.sh --zookeeper hadoop03:2181 --delete --topic first
      

      这里指定一下topic名称就可以了。

    发送和接收消息

    首先是发送:

     bin/kafka-console-producer.sh --broker-list hadoop03:9092 --topic first
    

    这里要指定主机名和要发送到的topic

    然后是接收:

     bin/kafka-console-consumer.sh --zookeeper hadoop03:2181 --topic first
    

    但这种方式提示已经过时了,我们采用新的:

     bin/kafka-console-consumer.sh --bootstrap-server hadoop03:9092 --topic first
    

    这样我们就可以在控制台来测试kafka了。

    如果想看到之前没开启消费者时发送的内容,可以加一条参数:

     bin/kafka-console-consumer.sh --bootstrap-server hadoop03:9092 --from-beginning --topic first
    

    就可以看到了。

    api操作

    总的来说,我们使用kafka还是用api居多,这里就介绍一下最基本的API操作。

    首先要导入依赖:

    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>0.11.0.0</version>
    </dependency>
    

    生产者——异步发送API

    直接上代码:

    package com.liuge.producer;
    
    import org.apache.kafka.clients.producer.KafkaProducer;
    import org.apache.kafka.clients.producer.ProducerConfig;
    import org.apache.kafka.clients.producer.ProducerRecord;
    
    import java.util.Properties;
    
    /**
     * @ClassName: MyProducer
     * @Description: kafka生产者类
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class MyProducer {
    
        public static void main(String[] args) {
            // 1.创建kafka生产者的配置信息
            Properties properties = new Properties();
            // 2.指定连接的kafka集群
            properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop03:9092");
            // 3.ack应答级别
            properties.put(ProducerConfig.ACKS_CONFIG, "all");
    
            // 4.重试次数
            properties.put(ProducerConfig.RETRIES_CONFIG, 3);
    
            // 5.批次大小
            properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
    
            // 6.等待时间
            properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
    
            // 7.RecordAccumulator 缓冲区大小
            properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
            // 8.key,value的序列化类
            properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
            properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization" +
                    ".StringSerializer");
            // 9.创建生产者对象
            KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
            // 10.发送数据
            for (int i = 0; i < 10; i++) {
                producer.send(new ProducerRecord<>("first","liuge--" + i));
            }
    
            // 11.关闭资源
            producer.close();
    
        }
    }
    
    

    代码很简单,就不过多解释了。

    生产者——异步发送API-带回调

    package com.liuge.producer;
    
    import org.apache.kafka.clients.producer.*;
    
    import java.util.Properties;
    
    /**
     * @ClassName: CallBackProducer
     * @Description: 带回调的生产者
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class CallBackProducer {
    
        public static void main(String[] args) {
            // 1.创建配置信息
            Properties properties = new Properties();
            properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop03:9092");
            properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
            properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
    
            // 2.创建生产者对象
            KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
    
            // 3.发送数据
            for (int i = 0; i < 10; i++) {
                producer.send(new ProducerRecord<>("first", 0,"liuge","liuge--" + i), (metadata, exception) -> {
                    if (exception == null) {
                        System.out.println("partition==>" + metadata.partition());
                        System.out.println("offset==>" + metadata.offset());
                    }
                });
            }
    
            // 4.关闭资源
            producer.close();
    
        }
    }
    

    生产者——自定义分区器

    首先是自定义分区器

    package com.liuge.patitioner;
    
    import org.apache.kafka.clients.producer.Partitioner;
    import org.apache.kafka.common.Cluster;
    
    import java.util.Map;
    
    /**
     * @ClassName: MyPartitioner
     * @Description: 自定义分区器
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class MyPartitioner implements Partitioner {
    
        @Override
        public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
            return 1;
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void configure(Map<String, ?> configs) {
    
        }
    }
    

    然后我们要调用这个自定义分区器:

    package com.liuge.producer;
    
    import org.apache.kafka.clients.producer.KafkaProducer;
    import org.apache.kafka.clients.producer.ProducerConfig;
    import org.apache.kafka.clients.producer.ProducerRecord;
    
    import java.util.Properties;
    
    /**
     * @ClassName: PartitionProducer
     * @Description: 自定义分区的生产者
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class PartitionProducer {
    
        public static void main(String[] args) {
            // 1.创建配置信息
            Properties properties = new Properties();
            properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop03:9092");
            properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
            properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
            // 添加分区器
            properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,"com.liuge.patitioner.MyPartitioner");
    
            // 2.创建生产者对象
            KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
    
            // 3.发送数据
            for (int i = 0; i < 10; i++) {
                producer.send(new ProducerRecord<>("first", "liuge--" + i), (metadata, exception) -> {
                    if (exception == null) {
                        System.out.println("partition==>" + metadata.partition());
                        System.out.println("offset==>" + metadata.offset());
                    }else{
                        exception.printStackTrace();
                    }
                });
            }
            // 4.关闭资源
            producer.close();
        }
    }
    

    生产者——自定义过滤器

    首先定义两个过滤器:

    package com.liuge.interceptor;
    
    import org.apache.kafka.clients.producer.ProducerInterceptor;
    import org.apache.kafka.clients.producer.ProducerRecord;
    import org.apache.kafka.clients.producer.RecordMetadata;
    
    import java.util.Map;
    
    /**
     * @ClassName: CounterInterceptor
     * @Description:
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class CounterInterceptor implements ProducerInterceptor<String ,String> {
    
        int success;
        int error;
    
        @Override
        public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
            return record;
        }
    
        @Override
        public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
            if(metadata!= null){
                success++;
            }else{
                error++;
            }
        }
    
        @Override
        public void close() {
            System.out.println("success = " + success);
            System.out.println("error = " + error);
        }
    
        @Override
        public void configure(Map<String, ?> configs) {
    
        }
    }
    
    package com.liuge.interceptor;
    
    import org.apache.kafka.clients.producer.ProducerInterceptor;
    import org.apache.kafka.clients.producer.ProducerRecord;
    import org.apache.kafka.clients.producer.RecordMetadata;
    
    import java.util.Map;
    
    /**
     * @ClassName: TimeInterceptor
     * @Description:
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class TimeInterceptor implements ProducerInterceptor<String,String> {
    
        @Override
        public void configure(Map<String, ?> configs) {
    
        }
    
        @Override
        public ProducerRecord<String,String> onSend(ProducerRecord<String,String> record) {
            // 1.取出数据
            String value = record.value();
    
            // 2.创建一个新的ProducerRecord对象
    
            return new ProducerRecord<>(record.topic(),record.partition(),record.key(),
                    System.currentTimeMillis() + "," + value);
        }
    
        @Override
        public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
    
        }
    
        @Override
        public void close() {
    
        }
    
    
    }
    

    然后在生产者中应用这两个过滤器:

    package com.liuge.producer;
    
    import org.apache.kafka.clients.producer.KafkaProducer;
    import org.apache.kafka.clients.producer.ProducerConfig;
    import org.apache.kafka.clients.producer.ProducerRecord;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Properties;
    
    /**
     * @ClassName: InterceptorProducer
     * @Description:
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class InterceptorProducer {
    
        public static void main(String[] args) {
            // 1.创建kafka生产者的配置信息
            Properties properties = new Properties();
            // 2.指定连接的kafka集群
            properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop03:9092");
            // 3.ack应答级别
            properties.put(ProducerConfig.ACKS_CONFIG, "all");
    
            // 4.重试次数
            properties.put(ProducerConfig.RETRIES_CONFIG, 3);
    
            // 5.批次大小
            properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
    
            // 6.等待时间
            properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
    
            // 7.RecordAccumulator 缓冲区大小
            properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
            // 8.key,value的序列化类
            properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
            properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
            List<String> interceptors = new ArrayList<>();
            interceptors.add("com.liuge.interceptor.TimeInterceptor");
            interceptors.add("com.liuge.interceptor.CounterInterceptor");
            properties.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,interceptors);
            // 9.创建生产者对象
            KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
            // 10.发送数据
            for (int i = 0; i < 10; i++) {
                producer.send(new ProducerRecord<>("first","liuge--" + i));
            }
            // 11.关闭资源
            producer.close();
        }
    }
    

    消费者——异步消费api

    package com.liuge.consumer;
    
    import org.apache.kafka.clients.consumer.ConsumerConfig;
    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;
    
    /**
     * @ClassName: MyConsumer
     * @Description: 测试消费者
     * @author: LiuGe
     * @date: 2021/1/17
     */
    public class MyConsumer {
    
        public static void main(String[] args) {
            // 1.创建消费者配置信息
            Properties properties = new Properties();
            // 2.给配置信息赋值
            // 连接的集群
            properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop03:9092");
            // 开启自动提交
            properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true);
            // 自动提交的延迟
            properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG,"1000");
            // key,value的反序列化
            properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringDeserializer");
            properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringDeserializer");
            // 消费者组
            properties.put(ConsumerConfig.GROUP_ID_CONFIG,"bigdata1");
    
            // 重置消费者的offset
    //        properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
    
            // 创建消费者
            KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
    
            // 订阅主题
            consumer.subscribe(Arrays.asList("first","second"));
    
            // 获取数据
            while (true){
                ConsumerRecords<String, String> consumerRecords = consumer.poll(100);
                // 解析并打印
                for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                    System.out.println(consumerRecord.key() + "-->" + consumerRecord.value());
                }
            }
        }
    }
    

    总结

    总的来说,kafka的使用难度不是很大,但要了解其原理还是很复杂的。本文章只是简单记录了一下kafka的常用用法,没有涉及太复杂的内容。

  • 相关阅读:
    在python中处理XML
    正则表达式
    python 反射
    python模块概况,json/pickle,time/datetime,logging
    python装饰器
    python3内置函数详解
    python函数,lambda表达式,三目运算,列表解析,递归
    python数据类型及其常用方法
    python3的文件操作
    初识Docker
  • 原文地址:https://www.cnblogs.com/wushenjiang/p/14290480.html
Copyright © 2020-2023  润新知