Kafka实战《原理一》

Kafka实战《原理一》,第1张

定义

传统定义:Kafka是一个分布式的基于发布订阅模式的消息队列,主要用于大数据实时处理领域。
最新定义: Kafka是一个开源的分布式事件流平台,主要用于高性能的数据通道,流分析,数据集成和关键任务应用。

消息队列

应用场景

1,异步处理
2,系统解耦
3,流量削峰
4,日志处理
5,消息通讯

两种模式

点对点模式: 消费者主动拉取数据,消息收到后清除消息

发布/订阅模式:

- 可以有个多个topic主题
- 消费者消费数据之后,不删除数据
- 每个消费者独立,都可以消费数据

kafka架构


1)Producer: 消息生产者,就是向kafka broker 发消息的客户端
2)Consumer: 消息消费者,向Kafka broker取消息的客户端
3)Consumer Group: 消费者组,由多个消费者组成。每个消费者负责消费不同分区的数据,一个分区只能有一个组内消费者消费;消费者组之间相互不影响。
4)Broker:一台kafka服务器就是一个broker。一个集群有多个broker组成。一个broker可以容纳多个topic。
5)Topic:可以理解为一个队列,生产者和消费者面向都是一个Topic。
6)Partition:为了实现扩展性,一个非常大的topic可以分不到多个broker上,一个topic可以分为多个partition,每个partition是一个有序的队列。
7)Replica:副本,一个topic的每个分区都有若干个副本,一个leader和若干个Follwoer。
8)Leader:每个分区的多个副本的主,生产者发送数据的对象及消费者消费数据的对象都是Leader。
9)Follwower: 每个分区多个副本的从,实时的从Leader中同步数据,保持和Leader的数据同步。Leader发生故障的时候,某个Follower会成为新的Leader。

集群部署

1 官方下载地址
2 配置

#创建目录
mkdir  /opt/module/
#解压
tar -zxvf kafka_2.12-3.0.0.tgz -C  /opt/module/
# 进入module目录并修改目录名称
cd  /opt/module/
mv kafka_2.12-3.0.0/   kafka
#修改配置文件
cd  config/
vim server.properties

输入一下内容


3 分别在 ah102和ah103 上修改配置文件/opt/module/kafka/config/server.properties
中的 broker.id=1、 broker.id=2
注: broker.id 不得重复,整个集群中唯一。

4 启动集群
4.1 启动zk集群
4.2 启动kafka集群

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

集群启停脚本
vi kf.sh

#! /bin/bash
case  in
"start"){
for i in hadoop102 hadoop103 hadoop104
do
echo " --------启动 $i Kafka-------"
ssh $i "/opt/module/kafka/bin/kafka-server-start.sh -
daemon /opt/module/kafka/config/server.properties"
done
};;
"stop"){
for i in ah101  ah102 ah103
do
echo " --------停止 $i Kafka-------"
ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh "
done
};;
esac
# 添加权限
chmod -x kf.sh
#启动命令 
 kf.sh start
 #停止命令
 kf.sh stop

注意: 停止 Kafka 集群时,一定要等 Kafka 所有节点进程全部停止后再停止 Zookeeper
集群。因为 Zookeeper 集群当中记录着 Kafka 集群相关信息, Zookeeper 集群一旦先停止,
Kafka 集群就没有办法再获取停止进程的信息,只能手动杀死 Kafka 进程了

kafka命令行 *** 作

1) 主题命令行 *** 作(kafka-topic.sh)

1 查看当前服务器中的所有 topic

bin/kafka-topics.sh --bootstrap-server ah101:9092 --list

2 创建 test topic

bin/kafka-topics.sh --bootstrap-server ah101:9092 --create --partitions 1 --replication-factor 3 --topic test

选项说明:
–topic 定义 topic 名
–replication-factor 定义副本数
–partitions 定义分区数

3 查看主题详情

bin/kafka-topic.sh  --bootstrap-server ah101:9092 --describe --topic test

4 修改分区数(注意分区数只能增加,不能减少)

bin/kafka-topics.sh --bootstrap-server ah101:9092 --alter  --topic test --partiton  3

4 删除topic

bin/kafka-topics.sh --bootstrap-server ah101:9092 --delete  --topic test 

2) 生产者命令行 *** 作(kafka-console-producer.sh)

1 发送消息

bin/kafka-console-producer.sh --bootstrap-server ah101:9092 --topic test

3) 消费者命令行 *** 作(kafka-console-consumer.sh)


1 消费test主题中的数据

bin/kafka-console-consumer.sh   --bootstrap-server ah101:9092  --topic test

2 消费test主题中的所有的数据

bin/kafka-console-consumer.sh   --bootstrap-server ah101:9092  --from-beginning  --topic test

kafka生产者

一 发送原理
消息发送过程中,涉及到两个线程 main线程和Sender线程。main线程创建一个双端队列RecordAccumulator。main线程将消息发送给RecordAcuumulator,sender线程不断从RecordAccumulator中拉取消息到Kafka Broker。

二 重要的参数列表

参数名称描述
bootstrap.servers生产者连接集群所需的broker地址清单。例如:ah101:9092,ah102:9092,ah103:9092,可以设置一个或者多个,中间用逗号隔开。注意这里并非给所有的broker地址,因为生产者可以从给定的broker找到其他的broker信息
key.serializer和value.serilizer指定发送消息的key和value的序列化类型。一定要写全类名
buffer.memoryRecordAccumulator缓冲区总大小,默认32M
batch.size缓冲区一批数据的最大值,默认16k。适当增加该值,可以提高吞吐量,但是如果该值设置太大,会导致数据的传输延迟增加
linger.msr如果数据迟迟未达到batch.size,sender线程等待linger.ms时间后就会发送数据。单位ms,默认值0ms,表示没有延迟,生产者建议该值大小设置5-100ms
acks0:生产者发送过来的数据,不需要等数据落盘应答;1:生产者发送过来的数据,Leader收到数据后应答;-1(all):生产者发送过来的数据,Leader和ISR队列中所有的数据收齐数据后应答。默认-1
max.in.flight.requests.per.connection允许最多没有返回ack的次数,默认5,开启幂等性要保证该值是1-5的数字
retries当消息发送错误的时候,系统会重发消息。retries表示重试次数,默认int的最大值 2147483467.如果设置了重试,还想保证消息的有序性。需要设置MAX-IN-FLIGHT-REQUESTS-PER-CONNECTION=1,否则重试此失败消息的时候,其他消息可能发送成功
retry.backoff.msl两次重试之间的间隔,默认100ms
enable.idempotence是否开启幂等性,默认true
compression.type生产者发送所有的数据的压缩方式。默认none不压缩 支持类型: none,gzip,snappy,lz4,zstd

pom

       
            org.apache.kafka
            kafka_2.13
            3.0.0
        

三 异步发送API
1 普通异步发送

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;

/**
 * @date 2022/05/07 21:38:10
 */
public class CustomProducer {
    public static void main(String[] args) {
        //1.创建kafk配置对象
        Properties properties = new Properties();
        //2. 添加配置信息
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "ah101:9092,ah102:9092");
        //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");

        //3.传教Kafka生产者对象
        KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
        //4.调用send方法发送消息
        kafkaProducer.send(new ProducerRecord<>("test", "hello kafka"));
        //5.g关闭资源
        kafkaProducer.close();
    }
}

2 带回调异步发送

import org.apache.kafka.clients.producer.*;
import java.util.Objects;
import java.util.Properties;

/**
 * @author gusteau.qin@dbappsecurity.com.cn
 * @date 2022/05/07 21:38:10
 */
public class CustomCallbackProducer {

    public static void main(String[] args) {
        //1.创建kafk配置对象
        Properties properties = new Properties();
        //2. 添加配置信息
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "ah101:9092,ah102:9092");
        //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");

        //3.传教Kafka生产者对象
        KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
        //4.调用send方法发送消息
        kafkaProducer.send(new ProducerRecord<>("test", "hello kafka"), new Callback() {
            @Override
            public void onCompletion(RecordMetadata metadata, Exception exception) {
                if(Objects.isNull(exception)){
                    //无异常  注意消息发送失败后会自动重试,不需要我们在回调函数中手动重试
                    System.out.println("主题: "+metadata.topic()+"-------->>>>>分区:"+metadata.partition());
                }else{
                    //打印异常
                    exception.printStackTrace();
                }
            }
        });
        //5.g关闭资源
        kafkaProducer.close();
    }
}

四 同步发送API
只需要在异步发送的基础上,在调用下get()方法

import org.apache.kafka.clients.producer.*;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ExecutionException;

/**
 * @author gusteau.qin@dbappsecurity.com.cn
 * @date 2022/05/07 21:38:10
 */
public class CustomCallbackProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建kafk配置对象
        Properties properties = new Properties();
        //2. 添加配置信息
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "ah101:9092,ah102:9092");
        //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");

        //3.传教Kafka生产者对象
        KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
        //4.调用send方法发送消息
        kafkaProducer.send(new ProducerRecord<>("test", "hello kafka"), new Callback() {
            @Override
            public void onCompletion(RecordMetadata metadata, Exception exception) {
                if(Objects.isNull(exception)){
                    //无异常  注意消息发送失败后会自动重试,不需要我们在回调函数中手动重试
                    System.out.println("主题: "+metadata.topic()+"-------->>>>>分区:"+metadata.partition());
                }else{
                    //打印异常
                    exception.printStackTrace();
                }
            }
        }).get();
        //5.关闭资源
        kafkaProducer.close();
    }
}

五 生产者分区的好处
(1)便于合理使用资源。每个partition在一个broker存储,可以把海量数据按照分区切割成一块一块数据存储在多台broker上。合理的控制分区任务,可以实现负载均衡的效果。
(2)提高并行度,生产者可以以分区为单位发送数据,消费者可以以分区为单位消费数据。

六 生产者发送消息的分区策略

自定义分区器
1)需求
例如我们实现一个分区器实现, 发送过来的数据中如果包含 atguigu,就发往 0 号分区,不包含 hello,就发往 1 号分区。
2)实现步骤
(1)定义类实现 Partitioner 接口。
(2)重写 partition()方法。

自定义分区类

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;

import java.util.Map;

/**
 * @author gusteau.qin@dbappsecurity.com.cn
 * @date 2022/05/07 22:08:52
 */
public class MyPatitioner implements Partitioner {
    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        //获取消息
        String msg = value.toString();
        //partition
        int partition;
        if (msg.contains("hello")) {
            partition = 0;
        } else {
            partition = 1;
        }
        //返回分区号
        return partition;
    }

    //关闭
    @Override
    public void close() {

    }

    //配置方法
    @Override
    public void configure(Map configs) {

    }
}

使用

import org.apache.kafka.clients.producer.*;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ExecutionException;

/**
 * @author gusteau.qin@dbappsecurity.com.cn
 * @date 2022/05/07 21:38:10
 */
public class CustomPartitionerProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建kafk配置对象
        Properties properties = new Properties();
        //2. 添加配置信息
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "ah101:9092,ah102:9092");
        //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");
        //添加自定义分区
        properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,"org.apache.kafka.clients.producer.Partitioner.MyPatitioner");

        //3.传教Kafka生产者对象
        KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
        //4.调用send方法发送消息
        kafkaProducer.send(new ProducerRecord<>("test", "hello kafka"), new Callback() {
            @Override
            public void onCompletion(RecordMetadata metadata, Exception exception) {
                if(Objects.isNull(exception)){
                    //无异常  注意消息发送失败后会自动重试,不需要我们在回调函数中手动重试
                    System.out.println("主题: "+metadata.topic()+"-------->>>>>分区:"+metadata.partition());
                }else{
                    //打印异常
                    exception.printStackTrace();
                }
            }
        }).get();
        //5.关闭资源
        kafkaProducer.close();
    }
}

七 生产者如何提高吞吐量

根据环境修改调试一下参数配置:
batch.size:批次大小,默认16k
linger.ms: 等待时间,修改5-100ms
compression.type :压缩类型
RecordAccumulator: 缓冲区大小

八 生产经验

1,数据可靠性
ACK应答原理(0,1,-1)

注意: 在生产环境中 acks=0很少使用,acks=1一般用于传输普通日志,允许丢失个别数据,acks=1 一般用户传输和钱相关的数据,对可靠性要求比较高的场景。

数据可靠性条件=ACK级别设置为-1+分区副本大于等于2+ISR里应答的最小副本数量大于等于2

思考:Leader收到数据后,所有的Follower都开始同步数据,但是有一个Follower因为某种故障,迟迟不能与Leader同步,怎么解决?
Leader维护了一个动态的in-sync replica set(ISR)列表,意和Leader保持同步的Leader和Follower的集合。如果Follower长时间未向Leader发送通信请求或同步数据,则该Follower会被踢出ISR列表。该时间阈值由replica.lag.time.max.ms参数设定,默认30s

//acks参数设置
  properties.put(ProducerConfig.ACKS_CONFIG,"all");
  //重试参数设置
        properties.put(ProducerConfig.RETRIES_CONFIG,3);

2,数据传递语义

  • 至少一次(At Least Once):ack级别设置为-1+分区副本大于等于2+ISR的应答的最小副本大于等于2
  • 最多一次(At Most Once):ack级别设置为0
  • 精确一次(Exactly Once):幂等性+至少一次
  • 32

总结:
At Least Once:可以保证数据不丢失,但是不能保证数据重复。
At Most Once:可以保证数据不重复,但是不能保证数据不丢失。
Exactly Once:数据即不重复也不会丢失。kafka0.11版本引入重大特性 幂等性和事务。

3,幂等性
幂等性原理:

幂等性: Producer无论向broker发送多少重复数据,Broker端都会持久化一条,保证不重复。
重复数据判断的标准:具有相同主键提交时,Broker只会持久化一条,其中PID 时kafka每次重启都会分配一个新的,Partition表示分区号,sequence number是单调递增的。
所以幂等性 只能保证在单分区单会话内不重复。

幂等性使用:开启参数enable.idempotence,默认为true,false关闭。

4,生产者事务
Kafka事务原理:
说明: 开启事务,必须开启幂等性。Producer在使用事务之前,必须自定义一个唯一的事务id,这样,即使客户端挂掉,重启后也能继续完成未完成的事务。

/**
 *
 * @date 2022/05/07 21:38:10
 * 单个Producer,使用事务保证消息仅一次发送
 */
public class CustomTransactionProducer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建kafk配置对象
        Properties properties = new Properties();
        //2. 添加配置信息
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "ah101:9092,ah102:9092");
        //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");
        //设置事务id
        properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG,"transaction_id_0000");


        //3.传教Kafka生产者对象
        KafkaProducer kafkaProducer = new KafkaProducer<>(properties);
        //初始化事务
        kafkaProducer.initTransactions();
        //开启事务
        kafkaProducer.beginTransaction();
        //4.调用send方法发送消息
        try {
            kafkaProducer.send(new ProducerRecord<>("test", "hello kafka"));
            //提交事务
            kafkaProducer.commitTransaction();
        } catch (Exception e) {
            e.printStackTrace();
            //终止事务
            kafkaProducer.abortTransaction();
        } finally {
            //关闭资源
            kafkaProducer.close();
        }
    
    }
}

5,数据有序
单分区内,有序;
多分区,分区与分区之间无序;

  1. kafka在1.x版本之前保证数据单分区有序,条件如下:
    max.in.flight.requests.per.connection=1
    2)kafka在1.x版本以后版本保证数据单分区有序,条件如下:
  • 未开启幂等性, max.in.flight.requests.per.connection=1
  • 开启幂等性, max.in.flight.requests.per.connection设置小于等于5,
    原因:1.x之后,启用幂等性后,kafka服务端会缓存producer发来的最近5个request的元数据,故无论如何,都可以保证最近5个request都是有序的。

后续见》》》 Kafka实战《原理2》

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/877021.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存