简介: Kafka 是一个分布式消息队列系统,用于处理实时数据流。消息按照主题(Topic)进行分类存储,发送消息的实体称为 Producer,接收消息的实体称为 Consumer。Kafka 集群由多个 Kafka 实例(Server)组成,每个实例称为 Broker。
主要用途: 广泛应用于构建实时数据管道和流应用程序,适用于需要高吞吐量和低延迟的数据处理场景
依赖: Kafka 集群和消费者依赖 Zookeeper 集群来保存元数据信息(如偏移量、Broker 列表等),以确保系统的可用性和一致性。Zookeeper 是 Kafka 的协调服务,用于管理和监控 Kafka 的状态。
使用消息中间件的原因:缓冲与解耦
--from-beginning
标志从头开始消费。--group-id
参数加入消费组,Kafka 在服务端会存储分组名、主题和偏移量的映射关系,从而实现多消费者的协调消费。Kafka 广泛应用于各种需要处理大规模实时数据流的场景
官方下载地址:Kafka 下载
# 下载 Kafka 压缩包(以Scala 2.12 Kafka 3.8.0 版本为例)
wget https://downloads.apache.org/kafka/3.8.0/kafka_2.12-3.8.0.tgz
# 解压缩
tar -xzf kafka_2.12-3.8.0.tgz
# 重命名文件夹
mv kafka_2.12-3.8.0 kafka
在解压后的 Kafka 目录中,有几个重要的配置文件:
Zookeeper 是 Kafka 的分布式协调服务,Kafka 在启动前需要先启动 Zookeeper。
编辑 zookeeper.properties
文件:
cd kafka/config/
vim zookeeper.properties
确保以下配置正确:
dataDir=/tmp/zookeeper # 指定 Zookeeper 保存其数据的目录路径。
clientPort=2181 # 指定 Zookeeper 服务监听客户端连接的端口号。
maxClientCnxns=0 # 设置单个客户端 IP 地址对 Zookeeper 服务器的最大连接数, 设置为 0,则表示不限制单个客户端的连接数量
# Zookeeper 集群配置
# server.X=hostname:port1:port2
# Zookeeper 节点的主机名或 IP 地址,Zookeeper 节点间的通信的端口(通常是选举端口和仲裁端口)
# 每个 server.X 的配置必须在所有 Zookeeper 节点上保持一致
server.1=zookeeper1:2888:3888
server.2=zookeeper2:2888:3888
server.3=zookeeper3:2888:3888
编辑 server.properties
文件来配置 Kafka 服务器。
vim server.properties
常用配置项:
broker.id=0 # 每个 Kafka 服务器的唯一标识符
log.dirs=/tmp/kafka-logs # 存放日志的目录
zookeeper.connect=localhost:2181 # Zookeeper 的连接地址
# Zookeeper集群
# zookeeper.connect=zookeeper1:2181,zookeeper2:2181,zookeeper3:2181
Kafka 依赖 Zookeeper,因此需要先启动 Zookeeper。
# 启动 Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties
# 启动 Kafka 服务器
bin/kafka-server-start.sh config/server.properties
# 停止 Kafka 服务器
bin/kafka-server-stop.sh
# 停止 Zookeeper
bin/zookeeper-server-stop.sh
# --bootstrap-server参数就表示服务器的连接方式(必选)
# 创建主题 (这里创建主题名称为test,下同)
kafka-topics.sh --bootstrap-server localhost:9092 --create --topic test
# 查看所有主题
kafka-topics.sh --bootstrap-server localhost:9092 --list
# 查看指定主题信息
kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic test
# 修改指定主题的参数
# --partitions : 修改的配置参数:分区数量
kafka-topics.sh --bootstrap-server localhost:9092 --topic test --alter --partitions 2
# 删除指定名称的主题
kafka-topics.sh --bootstrap-server localhost:9092 --topic test --delete
# 消费者订阅主题
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test
# 生成者推送主题
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test
# 生产者批量推送文件
kafka-console-producer.sh --broker-list localhost:9092 --topic test < xxx.text
<dependency>
<groupId>org.apache.kafkagroupId>
<artifactId>kafka-clientsartifactId>
<version>3.6.1version>
dependency>
<dependency>
<groupId>org.apache.kafkagroupId>
<artifactId>kafka-streamsartifactId>
<version>3.6.1version>
dependency>
Admin API 用于管理 Kafka 集群、主题、分区等资源。
package kafkaAPI
import java.util.{Collections, Properties}
import org.apache.kafka.clients.admin.{AdminClient, AdminClientConfig, NewTopic}
object AdminAPI {
def main(args: Array[String]): Unit = {
// 创建配置属性
val props = new Properties()
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "master01:9092") // 设置 Kafka 集群的地址
// 创建 AdminClient 实例
val adminClient = AdminClient.create(props)
// 创建一个新的主题,主题名称为 "my-new-topic",有 1 个分区和 1 个副本
val newTopic = new NewTopic("my-new-topic", 1, 1.toShort)
// 创建该主题
adminClient.createTopics(Collections.singletonList(newTopic)).all().get()
// 关闭 AdminClient
adminClient.close()
}
}
生产者 API 用于将记录发布到一个或多个 Kafka 主题中。
package kafkaAPI
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerConfig, ProducerRecord}
import org.apache.kafka.common.serialization.StringSerializer
import java.util.Properties
object ProducerAPI {
def main(args: Array[String]): Unit = {
// 创建配置属性
val props = new Properties()
// 设置 Kafka 集群的地址
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "master01:9092")
// 设置键和值的序列化器
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, classOf[StringSerializer].getName)
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, classOf[StringSerializer].getName)
// 创建 KafkaProducer 实例
val producer = new KafkaProducer[String, String](props)
// 创建一个 ProducerRecord,指定主题、键和值
val record = new ProducerRecord[String, String]("my-topic", "key2", "value2")
// 发送消息
producer.send(record)
// 关闭 KafkaProducer
producer.close()
}
}
消费者 API 用于从 Kafka 主题中读取记录,支持自动和手动提交偏移量。
package kafkaAPI
import org.apache.kafka.clients.consumer.{ConsumerConfig, KafkaConsumer}
import org.apache.kafka.common.serialization.StringDeserializer
import java.time.Duration
import java.util.{Collections, Properties}
object ConsumerAPI {
def main(args: Array[String]): Unit = {
// 创建配置属性
val props = new Properties()
// 设置 Kafka 集群的地址
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "master02:9092")
// 设置消费者组 ID
props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group")
// 设置键和值的反序列化器
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
// 创建 KafkaConsumer 实例
val consumer = new KafkaConsumer[String, String](props)
// 订阅主题 "my-topic"
consumer.subscribe(Collections.singletonList("my-topic"))
// 不断地轮询 Kafka 消息
while (true) {
// 轮询消息,等待时间为 100 毫秒
val records = consumer.poll(Duration.ofMillis(100))
// 处理每条消息
records.forEach(record => {
println(s"offset = ${record.offset()}, key = ${record.key()}, value = ${record.value()}")
})
}
}
}
Streams API 用于构建具有状态和无状态的流处理应用程序。
package kafkaAPI
import java.util.Properties
import org.apache.kafka.streams.{KafkaStreams, StreamsBuilder, StreamsConfig}
import org.apache.kafka.streams.kstream.KStream
import org.apache.kafka.common.serialization.Serdes
object StreamsAPI {
def main(args: Array[String]): Unit = {
// 创建配置属性
val props = new Properties()
// 设置应用 ID,这将作为 Kafka Streams 应用程序的标识符
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "my-stream")
// 设置 Kafka 集群的地址
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "master01:9092")
// 设置默认键和值的 Serde 类
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass.getName)
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass.getName)
// 创建 StreamsBuilder 实例
val builder = new StreamsBuilder()
// 从 Kafka 主题 "input-topic" 创建一个 KStream 实例
val stream: KStream[String, String] = builder.stream("input-topic")
// 处理数据,在值后拼接 "-out"
val processedStream: KStream[String, String] = stream.mapValues(value => value + "-out")
// 将处理后的数据发送到 Kafka 主题 "output-topic"
processedStream.to("output-topic")
// 创建 KafkaStreams 实例,并启动流处理应用程序
val streams = new KafkaStreams(builder.build(), props)
streams.start()
}
}
Kafka 能够在大数据生态系统中充当数据流的核心传输和处理管道。
Kafka 可以与多种大数据技术集成,以实现强大的数据流处理能力。
与 Hadoop 集成,通过 Kafka Connect 将数据导入 HDFS 进行批量处理;
与 Spark 集成,使用 Spark Streaming 进行实时数据处理;
与 Flink 集成,提供低延迟流处理和复杂事件处理;
与 Elasticsearch 集成,实现实时数据索引和搜索;
与 MongoDB 集成,进行实时数据存储和查询;
以及与 Redis 集成,用于实时缓存和快速数据访问。
Flume 可以将数据从各种来源传输到 Kafka 集群中,然后 Kafka 再将这些数据传输到其他系统进行处理和存储
flume采集数据到Kafka的配置
# 定义组件
a1.sources = r1
a1.channels = c1
# 配置source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
# 日志(数据)文件 监控test.log文件
a1.sources.r1.filegroups.f1 = /opt/module/data/test.log
a1.sources.r1.positionFile = /opt/software/flume-1.9.0/taildir_position.json
# 配置channel
# 采用Kafka Channel,省去了Sink,提高了效率
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = single01:9092
a1.channels.c1.kafka.topic = test
a1.channels.c1.parseAsFlumeEvent = false
# 组装
a1.sources.r1.channels = c1
执行flume操作采集数据到Kafka
# 进入flume
cd /opt/module/flume
# 执行
bin/flume-ng agent -n a1 -c conf/ -f job/file_to_kafka.conf
bin/flume-ng agent
启动 Flume 代理。
-n a1
指定代理名称为 a1
。
-c conf/
指定配置目录为 conf/
。
-f job/file_to_kafka.conf
指定具体的 Flume 配置文件。
使用 Spark Streaming 进行实时数据处理
<spark.scala.version>2.12spark.scala.version>
<dependency>
<groupId>org.apache.sparkgroupId>
<artifactId>spark-core_${spark.scala.version}artifactId>
<version>${spark.version}version>
dependency>
<dependency>
<groupId>org.apache.sparkgroupId>
<artifactId>spark-streaming_${spark.scala.version}artifactId>
<version>${spark.version}version>
dependency>
<dependency>
<groupId>org.apache.sparkgroupId>
<artifactId>spark-streaming-kafka-0-10_${spark.scala.version}artifactId>
<version>${spark.kafka.version}version>
dependency>
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.kafka.common.serialization.StringDeserializer
import shaded.parquet.org.codehaus.jackson.map.deser.std.StdDeserializer.IntegerDeserializer
object KafkaSparkStream {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("spark-streaming-kafka-01")
.setMaster("local[*]")
val spark: SparkSession = SparkSession
.builder()
.config(conf)
.getOrCreate()
import spark.implicits._
import org.apache.spark.sql.functions._
// 每3秒处理一次数据
val ssc = new StreamingContext(spark.sparkContext, Seconds(5))
val topic = "test"
val kafkaParams = Map(
(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hostname:9092"),
(ConsumerConfig.GROUP_ID_CONFIG, "group01"),
(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName),
(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName),
)
// 创建Kafka Direct Stream
KafkaUtils.createDirectStream[Int, String](
ssc,
LocationStrategies.PreferConsistent, // 分配策略
ConsumerStrategies.Subscribe[Int, String](Array(topic), kafkaParams) // 订阅主题
)
.foreachRDD(
// 对每个rdd处理
)
ssc.start()
ssc.awaitTermination()
}
}
Flink是分布式计算引擎,是一款非常强大的实时分布式计算框架,可以将Kafka作为数据源进行处理。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-javaartifactId>
<version>1.17.0version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-streaming-javaartifactId>
<version>1.17.0version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-clientsartifactId>
<version>1.17.0version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-connector-kafkaartifactId>
<version>1.17.0version>
dependency>
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer
import org.apache.flink.streaming.api.datastream.DataStream
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.flink.api.common.serialization.SimpleStringSchema
import java.util.Properties
object KafkaFlink {
def main(args: Array[String]): Unit = {
// 创建 Flink 流执行环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
// 配置 Kafka 消费者属性
val properties = new Properties()
properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092")
properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "test")
properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, classOf[StringDeserializer].getName)
// 创建 FlinkKafkaConsumer
val consumer = new FlinkKafkaConsumer[String]("my-topic", new SimpleStringSchema(), properties)
val stream: DataStream[String] = env.addSource(consumer)
// 打印数据流
stream.print()
// 执行 Flink 作业
env.execute("Flink Kafka Example")
}
}