定义
传统定义: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 $1 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.memory | RecordAccumulator缓冲区总大小,默认32M | batch.size | 缓冲区一批数据的最大值,默认16k。适当增加该值,可以提高吞吐量,但是如果该值设置太大,会导致数据的传输延迟增加 | linger.ms | r如果数据迟迟未达到batch.size,sender线程等待linger.ms时间后就会发送数据。单位ms,默认值0ms,表示没有延迟,生产者建议该值大小设置5-100ms | acks | 0:生产者发送过来的数据,不需要等数据落盘应答;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.ms | l两次重试之间的间隔,默认100ms | enable.idempotence | 是否开启幂等性,默认true | compression.type | 生产者发送所有的数据的压缩方式。默认none不压缩 支持类型: none,gzip,snappy,lz4,zstd |
pom
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.13</artifactId>
<version>3.0.0</version>
</dependency>
三 异步发送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<Object, Object> 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<Object, Object> 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<Object, Object> 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<String, ?> 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<Object, Object> 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端都会持久化一条,保证不重复。 重复数据判断的标准:具有<PID,Partition,seqNumber>相同主键提交时,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<Object, Object> 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,数据有序 单分区内,有序; 多分区,分区与分区之间无序;
- 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》
|