整体框架思维导图请访问:https://download.csdn.net/download/qq_38705144/14057057
1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
2)发布/订阅模式(一对多,消费者消费数据之后不会清除消息)
producer
consumer
consumer group
broker
topic
partition
replica
leader
follower
#! /bin/bash
#1、判断参数是否传入
#$@ - 把所有的输入参数当做个体 a.sh hello spark scala
#$# - 获取参数的个数
#$* - 把所有的输入参数当做整体
#报错:/bin/bash^M: 坏的解释器: 没有那个文件或目录
#运行命令 sed -i 's/\r$//' kafkaCluster
if [ $# -lt 1 ]
then
echo "必须输入一个参数...."
exit
fi
#2、根据传入的参数匹配逻辑
case $1 in
"start")
for host in hadoop102 hadoop103 hadoop104
do
echo "=================start $host kafka=============="
ssh $host "/opt/module/kafka/bin/kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties"
done
;;
"stop")
for host in hadoop102 hadoop103 hadoop104
do
echo "=================stop $host kafka=============="
ssh $host "/opt/module/kafka/bin/kafka-server-stop.sh"
done
;;
"status")
for host in hadoop102 hadoop103 hadoop104
do
pid=$(ssh $host "ps -ef|grep kafka |grep -v grep")
[ "$pid" ] && echo "$host kafka进程正常" || echo "$host kafka进程不存在或者异常"
done
;;
*)
echo "参数输入错误....."
;;
esac
启动
kafka-server-start.sh -daemon /opt/module/kafka/config/server.properties
-daemon:实现后台运行
/opt/module/kafka/config/server.properties 启动所需的配置文件
停止
kafka-server-stop.sh
创建topic
kafka-topics.sh --create --bootstrap-server hadoop102:9092,hadoop103:9092 --topic first --partitions 2 --replication-factor 3
–bootstrap-server:使用的集群(可以设置一个,当102宕机也可以访问)
–partitions:设置分区数
-replication-factor:副本数(默认1)
–topic 定义topic名
–create
–bootstrap-server
–partitions
–replication-factor
–topic
删除topic
kafka-topics.sh --delete --bootstrap-server hadoop102:9092,hadoop103:9092 --topic topic名
–delete
–bootstrap-server
–topic
查看所有topic
kafka-topics.sh --list --bootstrap-server hadoop102:9092,hadoop103:9092
–list :查看所有topic
查看topic详细信息
修改信息
–alter --partitions
生产者
kafka-console-produce
–topic
–broker-list
消费者
kafka-console-consumer
–topic
–bootstrap-server
消费者拉取数据默认是从消费者启动之后开始拉取数据
若拉取所有数据
–from-beginning
每次启动都是一个新的consumer
若创建新的消费者组,用创建的消费者组拉取数据,则不会拉取之前数据
流程
segment
index
将文件每隔一定区间建一个索引
官网index默认值:4k
offset:标识,满4k得到一个offset
position:物理存储地址
log
timeindex
分区策略
为什么分区?
原则
情况1:直接指定分区号
情况2:kv键值对,通过key的hashcode值%partitions的个数=分区号
情况3:默认
新版,黏性分区
旧版,轮询机制(合理)
数据可靠性
1、副本数据同步
副本全部完成同步,才发送ack
ISR
和leader保持同步的follower集合
ack机制
概述:topic的每个partition收到producer发送的数据后,都需要向producer发送ack
ack=0:不接受应答,不断发数据
ack=1:leader接收到数据发送ack
ack=-1:leader接受数据,follower同步完ack
故障细节处理
exactly once
幂等性(0.11版本引入)
Producer不论向Server发送多少次重复数据,Server端都只会持久化一条
开启方式:enable.idompotence
原理
主键约束
producerID+partitionID+SequenceNUM
producerID:生产者ID
partitionID:分区号
sequenceNUM:队列数据位置
要保证produce不变,否则会导致数据重复
消费方式
push(推送方式)
pull(拉取方式)
分区分配策略
range(轮询)
roundrobin
流程
1、producer将消息封装为producerRecoder
2、消息传递给拦截器(数据筛选、清洗)
3、序列化器,进行数据传输
4、分区器确定数据发送至哪个分区(数据以批次的方式进行打包)
5、数据进入共享变量recordAccumulator
6、send线程拉取数据发送到broker
异步发送
同步发送
程序流程
import org.apache.kafka.clients.producer.*;
import java.nio.channels.AsynchronousByteChannel;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
/**
* @ClassName : MyProducer
* @Author : kele
* @Date: 2021/1/8 14:10
* @Description :自定义生产者
* 1、创建一个生产者:producer
* 2、创建配置文件properties,包含生产者相关信息,
* key,value序列化方式
* ack的模式
* 运行的集群
* 3、将数据封装为ProducerRecord,
*/
public class MyProducer {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//异步发送,效率高
//AsySend();
//同步发送数据
SynSend();
}
/**
* 异步发送数据
*/
public static void AsySend(){
/**
* 2、设置配置信息
/
Properties props = new Properties();
props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer"); //设置发送数据的key序列化方式
props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer"); //设置发送数据的value序列化方式
props.put("acks","all"); //设置ack,可以为-1(all),0,1
props.put("bootstrap.servers","hadoop102:9092,hadoop103:9092"); //设置运行的集群
props.put("enable.idempotence","true"); //设置是否使用幂等性
/**
* 1、创建生产者,构造函数需要properties的配置信息
/
Producer producer = new KafkaProducer(props);
for (int i = 11; i <= 15; i++) {
/**
* 3、将数据封装为producerRecord的形式,
ProducerRecord ps = new ProducerRecord("first","send "+i+" message");
//4、发送数据
producer.send(ps);
}
producer.close();
}
/**
* 同步发送数据
* producer.send(ps, new Callback() {...}).get //通过.get方法获取数据,相当于阻塞
*/
public static void SynSend() throws ExecutionException, InterruptedException {
/**
* 2、设置配置信息
/
Properties props = new Properties();
props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer"); //设置发送数据的key序列化方式
props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer"); //设置发送数据的value序列化方式
props.put("acks","all"); //设置ack,可以为-1(all),0,1
props.put("bootstrap.servers","hadoop102:9092,hadoop103:9092"); //设置运行的集群
props.put("enable.idempotence","true"); //设置是否使用幂等性
/**
* 1、创建生产者,构造函数需要properties的配置信息
/
Producer producer = new KafkaProducer(props);
for (int i = 11; i <= 15; i++) {
/**
* 3、将数据封装为producerRecord的形式,
ProducerRecord ps = new ProducerRecord("first","send "+i+" message");
//4、发送数据,并且接受返回相关数据
System.out.println("send num: " + i);
producer.send(ps, new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
//获取返回的offset值
long offset = recordMetadata.offset();
//获取返回的分区信息
int partition = recordMetadata.partition();
//获取topic
String topic = recordMetadata.topic();
System.out.println("receive: topic-->"+topic+",partition-->"+partition+",offset-->"+offset);
}
}).get();
}
producer.close();
}
}
1、创建生产者对象(构造函数包含配置properties)
2、配置properties
3、数据封装成producerRecoder(topic,value)
/**
* @ClassName : MyCustomerTest
* @Author : kele
* @Date: 2021/1/8 19:56
* @Description :自定义customer
* 1、创建一个kafkaconsumer,构造方法需要参数类properties
* 1)、key,value序列化方式
* 2)、使用集群
* 3)、设置开始读取位置
* 4)、是否自动提交事务
* 2、确定consumer订阅的消息topic(集合的形式),myconsumer.subscribe(mycluster);
* 3、使用poll方法拉取数据(构造方法中设置时间间隔)
*/
public class MyCustomerTest {
public static void main(String[] args) {
Properties pro = new Properties();
pro.setProperty("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
pro.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
pro.setProperty("bootstrap.servers","hadoop102:9092,hadoop103:9092");
pro.setProperty("group.id","g2");
//配置开始读取数据的位置
pro.setProperty("auto.offset.reset","earliest");
//是否自动提交
pro.setProperty("enable.auto.commit", "false");
// 1、创建一个kafkaconsumer,构造方法需要参数类properties
KafkaConsumer<String, String> ms = new KafkaConsumer<>(pro);
while(true){
//确定从哪个topic获取数据
List<String> mycluster = new ArrayList<String>();
mycluster.add("first");
// 2、确定consumer订阅的消息topic(集合的形式),myconsumer.subscribe(mycluster);
ms.subscribe(mycluster);
// 3、使用poll方法拉取数据(构造方法中设置时间间隔)
ConsumerRecords<String, String> poll = ms.poll(Duration.ofSeconds(5));
Iterator<ConsumerRecord<String, String>> iterator = poll.iterator();
while(iterator.hasNext()){
ConsumerRecord<String, String> next = iterator.next();
String topic = next.topic();
String key = next.key();
String value = next.value();
long offset = next.offset();
System.out.println("receive: topic-->"+topic+",key-->"+key+",value-->"+value+",offset-->"+offset);
//异步提交
ms.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> map, Exception e) {
Iterator<Map.Entry<TopicPartition, OffsetAndMetadata>> iterator1 = map.entrySet().iterator();
while(iterator1.hasNext()){
Map.Entry<TopicPartition, OffsetAndMetadata> next1 = iterator1.next();
int partition = next1.getKey().partition();
String topic1 = next1.getKey().topic();
long offset1 = next1.getValue().offset();
System.out.println("partition-->"+partition+",topic-->"+topic1+",offset-->"+offset1);
}
}
});
}
}
}
}
- 1、创建一个kafkaconsumer,构造方法需要参数类properties
- 1)、key,value序列化方式
- 2)、使用集群
- 3)、设置开始读取位置
- 4)、是否自动提交事务
- 2、确定consumer订阅的消息topic(集合的形式),myconsumer.subscribe(mycluster);
- 3、使用poll方法拉取数据(构造方法中设置时间间隔)