kafka
一.概述
1.简单了解
Kafka : Kafka是 一个开源的分布式事件流平台 (Event Streaming Platform,被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用
目 前 企 业 中 比 较 常 见 的 消 息 队 列 产 品 主 要 有 Kafka、ActiveMQ、 RabbitMQ、RocketMQ等,在大数据场景主要采用Kafka作为消息队列
传统消息队列的应用场景
传统的消息队列的主要应用场景包括:缓存/消峰、解耦和异步通信
缓冲/消峰:有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况,下图,生产者发送消息的数据时100条/秒,二消费者处理消息的能力只有30条/秒,那么Kafka可以起到暂时缓冲消息的作用
解耦:允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束
比如Spark和Spring想和数据库打交道,就可以根据一个消息队列Q1完成
2.两种模式
(1)点对点模式
一个生产者,一个消费者,一个主题(存储消息的容器,是个逻辑概念,实际消息存在文件中),消费者主动拉取数据,消息收到后通知队列清除消息
(2)发布/订阅模式(常用)
多个生产者,多个消费者,可以有多个topic主题
消费者消费数据之后,Kafka集群不删除数据
每个消费者(消费者组整体看做一个消费者)相互独立,可以消费相同主题相同分区的数据,互不影响。特别注意,消费者组里消费者只能消费不同的分区
3.Kafka基础架构
broker:就是一台服务器
topic:kafka数据写入操作的基本单元
topic分区:提高topic可用性、可靠性
leader:每个分区的主(生产者、消费者都针对leader)
follower:每个分区的副本(副本的作用是保证可靠性)
group:消费者组,里面有单个的消费者
consumer:单个的消费者
1.为了提高吞吐量,一个topic(topic是Kafka数据写入操作的基本单元)被分成多个分区,producer发布数据时,必须指定将该消息发布到哪个topic。consumer订阅消息时,也必须指定订阅哪个topic的信息
2.消费者组,为了提高效率,每一个消费者组的消费者可以消费一个分区的数据,一对一的关系,同一个消费者组的消费者不可以消费同一个分区
3.为了保证可靠性,每个分区的数据有备份,备份称为follower,主数据是leader,无论生产还是消费,处理的对象都是leader,zookeeper可以帮助我们记录谁是leader,新版本的kafka已经不需要zookeeper了,但目前使用zookeeper还是比较多的
二.Kafka快速入门
1.安装Kafka
下载安装包
tar zxvf 解压
,重命名
bin目录里面是一些命令
config是配置文件
libs是一些依赖jar包
重要配置
broker.id kafka在集群中身份的唯一标识,不可以重复
log.dirs kafka文件存放的位置
第三个修改的地方
zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181/ka
fka
分发到其他服务器节点
修改另外两个节点的broker.id为1和2
sudo vim /etc/profile.d/my_env.sh
配置kafka环境变量
#KAFKA_HOME
export KAFKA_HOME=/opt/module/kafka
export PATH=$PATH:$KAFKA_HOME/bin
source /etc/profile
分发
sudo /home/gzhu/bin/xsync /etc/profile.d/my_env.sh
source /etc/profile
另外两台主机
source /etc/profile
启动zookeeper集群
在kafka目录的bin下编写kafka一键启动脚本
vim 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 hadoop102 hadoop103 hadoop104
do
echo " --------停止 $i Kafka-------"
ssh $i "/opt/module/kafka/bin/kafka-server-stop.sh "
done
;;
esac
添加执行权限
chmod +x kf.sh
2.命令操作Kafka
启动kafka
Topic命令
用 kafka-topics.sh
命令,后面跟一些参数
注意:前面的符号是- -
参数 | 说明 |
---|---|
–bootstrap-server <String: server toconnect to> | 连接kafka的主机和端口号 |
–topic name | 操作的topic名称 |
–create | 创建主题 |
–delete | 删除主题 |
–alter | 修改主题 |
–list | 查看所有主题 |
–describe | 查看主题详细信息 |
–partitions | 设置分区数 |
–replication-factor num | 设置分区副本 |
–config | 更新系统默认的配置 |
bin目录下:
./kafka-topics.sh --bootstrap-server hadoop102:9092 --topic first --create --partitions 1 --replication-factor 3
-bootstrap-server hadoop102:9092 连接集群
–topic first 指定主题名字
–create 创建主题
–partitions 1 分区为1
–replication-factor 3 主题为3
./kafka-topics.sh --bootstrap-server hadoop102:9092 --topic first --describe
查看主题的详细信息
./kafka-topics.sh --bootstrap-server hadoop102:9092 --topic first --alter --partitions 3
–partitions 3 意思是修改分区数为3,注意分区只能增加不能修改
分区副本虽有命令,但不能使用命令行的操作进行修改分区副本,暂且放一放
生产者发送数据
kafka-console-producer.sh
要指定集群连接和发送到哪个topic
[gzhu@hadoop102 bin]$ ./kafka-console-producer.sh --bootstrap-server hadoop102:9092 --topic first
消费者消费数据
kafka-console-consumer.sh
要指定集群连接和消费哪个topic
./kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic first
注意,消费者默认只会消费消费者启动后,生产者发送的数据,如果要消费历史数据,那么消费者应该加一个--from beginning
参数
3.异步发送
kafka异步发送指的外部数据到RecordAccumulator(一个存储消息的内存缓冲区)的发送,即外部数据发送到了RecordAccumulator就算成功 [ 具体理论可参考 kafka(二)]
导入依赖
<dependencies>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
以下测试是开启了消费者的情况下
普通异步发送 发送即忘模式
public class CustomProducer {
public static void main(String[] args) {
// 1.配置属性
Properties properties = new Properties();
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092");
// 指定对应的key和value序列化
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
// 2.创建生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
// 3.发送数据
for (int i = 0; i < 3; i++) {
producer.send(new ProducerRecord<>("topicx","data" + i));
}
// 4.关闭资源
producer.close();
}
}
可以看到,两个不同的消费者都消费到了主题topicx分区1的数据,也验证了每个消费者(消费者组整体看做一个消费者)相互独立,可以消费相同主题相同分区的数据,互不影响
回调异步发送
可以通过回调函数获取分区等信息,并能做异常处理,只有回调函数执行结束生产者才会结束,否则一致阻塞
// 3.发送数据
producer.send(new ProducerRecord<>("topicx","Producer to topicx -- callback"), new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if(e == null){
System.out.println("发送到的主题:" + recordMetadata.topic());
System.out.println("发送到的分区:"+recordMetadata.partition());
}
}
});
可以返回给生产者一些topic或者分区的信息
4.同步发送
kafka同步发送指的外部数据到kafka集群,kafka集群收到后正确反馈后才可以发送下一批数据
只需要在send后面加一个get方法就实现了同步发送,0是发送到哪个分区,""是发送的key,"Hello"是发送的数据,实际上,同步发送返回的是一个Future,通过get方法来获取返回结果,如果没有结果,将会一直阻塞
// 3.发送数据
producer.send(new ProducerRecord<>("topicx",0,"","Hello")).get();
5.订阅主题
务必配置消费者的id,不同的id视为不同的消费者,相同的id视为是一个消费者组
public class CustomConsumer {
public static void main(String[] args) {
// 1.配置
Properties properties = new Properties();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092");
// 2.反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class);
// 3.配置消费者id
properties.put(ConsumerConfig.GROUP_ID_CONFIG,"100");
// 4.创建消费者
KafkaConsumer<String,String> consumer = new KafkaConsumer<>(properties);
// 5.订阅主题
ArrayList<String> topics = new ArrayList<>();
topics.add("topicx");
consumer.subscribe(topics);
// 6.消费
while(true){
ConsumerRecords<String,String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> record : records) {
System.out.println(record.topic() +" " + record.partition() + " "+ record.value());
}
}
}
}
6.订阅某主题的分区
public class CustomConsumerPartition {
public static void main(String[] args) {
// 1.配置
Properties properties = new Properties();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092");
// 2.反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class);
// 3.配置消费者id
properties.put(ConsumerConfig.GROUP_ID_CONFIG,"kun100");
// 4.创建消费者
KafkaConsumer<String,String> consumer = new KafkaConsumer<>(properties);
// 5.订阅主题topicx的0号分区
ArrayList<TopicPartition> topicPartitions = new ArrayList<>();
topicPartitions.add(new TopicPartition("topicx",0));
consumer.assign(topicPartitions);
// 6.消费
while(true){
ConsumerRecords<String,String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> record : records) {
System.out.println(record);
}
}
}
}
7.大数据环境下的kafka工具类
public class MyKafkaUtil {
private static String brokers = "hadoop102:9092,hadoop103:9092,hadoop104:9092";
// kafka生产者
public static FlinkKafkaProducer<String> getKafkaProducer(String topic){
return new FlinkKafkaProducer<String>(brokers,topic,
new SimpleStringSchema());
}
// kafka生产者,主题可以变化,怎么使用在下面
public static <T> FlinkKafkaProducer<T> getKafkaProducer(KafkaSerializationSchema<T> kafkaSerializationSchema){
Properties properties = new Properties();
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,brokers);
return new FlinkKafkaProducer<T>("default_topic", // 默认主题,必须有,但是我们不用
kafkaSerializationSchema, // 一个kafkaSerializationSchema
properties, // 配置信息
FlinkKafkaProducer.Semantic.NONE);
}
// kafka消费者
public static FlinkKafkaConsumer<String> getKafkaConsumer(String topic,String groupId){
Properties properties = new Properties();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,brokers);
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class);
properties.put(ConsumerConfig.GROUP_ID_CONFIG,groupId);
// 没有offset,从新提交的数据开始消费 默认5s自动提交一次offset
properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"latest");
return new FlinkKafkaConsumer<>(topic, new SimpleStringSchema(), properties);
}
}
着重说一下主题自由变化的生产者,我们需要传入一个KafkaSerializationSchema,泛型可以自己定,这里我们是JSONObject
kafka.addSink(MyKafkaUtil.getKafkaProducer(new KafkaSerializationSchema<JSONObject>() {
@Override
public ProducerRecord<byte[], byte[]> serialize(JSONObject jsonObject, @Nullable Long aLong) {
return new ProducerRecord<byte[], byte[]>(topic,value);
}
}));
三.Kafka-Eagle可视化监控
1.环境准备
集群中要有MySQL,Kafka-Eagle 的安装依赖于MySQL,MySQL 主要用来存储可视化展示的数据
在kafka集群未启动的情况下
vim bin/kafka-server-start.sh
将
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi
修改为
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
export KAFKA_HEAP_OPTS="-server -Xms2G -Xmx2G
-XX:PermSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:ParallelGCThreads=8 -XX:ConcGCThreads=5
-XX:InitiatingHeapOccupancyPercent=70"
export JMX_PORT="9999"
#export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
fi
启动kafka集群
2.安装Kafka-eagle
解压
该文件下还有一个压缩包,再次解压
改名字
mv efak-web-2.0.8 efak
进入该目录下的conf目录
vim system-config.properties
将
改为,监控一个集群,信息在zookeeper
配置自己的mysql
mysql不要忘了创建ke数据库
添加环境变量
sudo vim /etc/profile.d/my_env.sh
# kafkaEFAK
export KE_HOME=/opt/module/efak
export PATH=$PATH:$KE_HOME/bin
source /etc/profile
启动
访问