目录
快速使用 Kafka
Kafka 依赖 Java 运行时环境,因此在启动 Kafka 或 ZooKeeper 之前,确保你的系统上已经安装了 Java。
通过以下步骤安装 OpenJDK:
- 更新包信息:
sudo apt update
- 安装 OpenJDK:
sudo apt install default-jdk
这将安装默认版本的 OpenJDK。如果你需要特定版本的 OpenJDK,可以使用相应的软件包名称。 - 安装完成后,验证 Java 安装是否成功:
java -version
- 重新运行 ZooKeeper 或 Kafka 命令。
步骤1:安装 Kafka
-
下载 Kafka: 前往 Apache Kafka 官方网站 下载最新的 Kafka 版本。
-
解压文件: 解压下载的 Kafka 压缩包到你选择的目录。
tar -xzf kafka_2.12-3.6.0.tgz
- -x: 表示解压缩。
- -z: 表示使用 gzip 解压。
- -f: 后面跟着压缩包的文件名。
-
启动 ZooKeeper: Kafka 依赖于 ZooKeeper,因此在启动 Kafka 之前,需要先启动 ZooKeeper。进入 Kafka 解压目录中的 bin 目录,执行以下命令启动 ZooKeeper:
./zookeeper-server-start.sh ../config/zookeeper.properties
-
启动 Kafka 服务器: 打开一个新的终端,进入 Kafka 解压目录中的 bin 目录,执行以下命令启动 Kafka 服务器:
./kafka-server-start.sh ../config/server.properties
步骤2:创建一个主题(Topic)
在 Kafka 中,数据被发布到主题(Topic)中,消费者订阅这些主题以接收数据。
- 创建一个主题: 打开一个新的终端,进入 Kafka 解压目录中的 bin 目录,执行以下命令创建一个名为 my-topic 的主题:
./kafka-topics.sh --create --topic my-topic --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1
--topic my-topic
指定创建的主题的名称为 “my-topic”。--bootstrap-server localhost:9092
指定用于创建主题的 Kafka 集群的引导服务器地址。--partitions 1
指定创建的主题将包含的分区数量。分区是 Kafka 中消息并行处理的单位。在这里,指定的分区数量是1,表示该主题只有一个分区。分区数量的选择通常取决于并行性需求和消息负载的分布。--replication-factor 1
指定每个分区的副本数量。在这里,指定的副本数量是1,表示每个分区只有一个副本。副本用于提高数据的可靠性和容错性。如果有多个副本,数据会在多个 broker 上进行复制。通常,你会希望设置大于1的副本数量,以确保数据的可靠性。
步骤3:生产者发送消息
在 Kafka 中,生产者负责将消息发布到主题。
-
启动生产者: 打开一个新的终端,进入 Kafka 解压目录中的 bin 目录,执行以下命令启动一个生产者:
./kafka-console-producer.sh --topic my-topic --bootstrap-server localhost:9092
-
发送消息: 在生产者终端中,输入一些消息并按回车键发送。
步骤4:消费者接收消息
在 Kafka 中,消费者订阅主题以接收生产者发送的消息。
-
启动消费者: 打开一个新的终端,进入 Kafka 解压目录中的 bin 目录,执行以下命令启动一个消费者:
./kafka-console-consumer.sh --topic my-topic --bootstrap-server localhost:9092 --from-beginning
--from-beginning
-
接收消息: 在消费者终端中,你将看到生产者发送的消息。
使用 kafka-go 实现生产者和消费者示例代码
生产者
package main
// ./kafka-console-producer.sh --topic my-topic --bootstrap-server localhost:9092
import (
"context"
"fmt"
"strconv"
"time"
"github.com/segmentio/kafka-go"
)
var (
reader *kafka.Reader
topic = "user_click"
)
func Write(ctx context.Context) {
writer := &kafka.Writer{
Addr: kafka.TCP("localhost:9092"),
Topic: topic,
Balancer: &kafka.Hash{
},
WriteTimeout: 1 * time.Second,
RequiredAcks: kafka.RequireNone,
AllowAutoTopicCreation: true,
}
defer writer.Close()
tm := time.Now().Unix()
for i := 0; i < 3; i++ {
if err := writer.WriteMessages(
ctx,
kafka.Message{
Key: []byte("1"), Value: []byte("小" + strconv.Itoa(int(tm)))},
kafka.Message{
Key: []byte("2"), Value: []byte("白" + strconv.Itoa(int(tm)))},
kafka.Message{
Key: []byte("3"), Value: []byte("小" + strconv.Itoa(int(tm)))},
kafka.Message{
Key: []byte("1"), Value: []byte("熊" + strconv.Itoa(int(tm)))},
kafka.Message{
Key: []byte("1"), Value: []byte("猫" + strconv.Itoa(int(tm)))},
); err != nil {
if err == kafka.LeaderNotAvailable {
time.Sleep(500 * time.Millisecond)
continue
} else {
fmt.Println("批量写kafka失败:%v \n", err)
}
} else {
break
}
}
}
func main() {
ctx := context.Background()
Write(ctx)
}
消费者
package main
// ./kafka-console-consumer.sh --topic user_click --bootstrap-server localhost:9092 --from-beginning --group a
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/segmentio/kafka-go"
)
var (
reader *kafka.Reader
topic = "user_click"
)
func read(ctx context.Context) {
reader := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{
"localhost:9092"},
Topic: topic,
CommitInterval: 1 * time.Second,
GroupID: "rec_team",
StartOffset: kafka.FirstOffset,
})
for {
if message, err := reader.ReadMessage(ctx); err != nil {
fmt.Println("读kafka失败: ", err)
break
} else {
fmt.Printf("topic=%s, partition=%d, offset=%d, key=%s, value=%s \n", message.Topic, message.Partition, message.Offset, string(message.Key), string(message.Value))
}
}
}
func listenSignal() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
sig := <-c
fmt.Println("接收到信号: ", sig.String())
if reader != nil {
reader.Close()
}
os.Exit(0)
}
func main() {
ctx := context.Background()
go listenSignal()
read(ctx)
}