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的常用用法,没有涉及太复杂的内容。

posted @ 2021-01-17 22:12  武神酱丶  阅读(100)  评论(0编辑  收藏  举报