网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
- 启动zookeeper
由于kafka是基于zookeeper做集群节点维护和管理的, 会把节点信息存到zk中, 所以要先启动zookeeper, kafka有自带的zookeeper
cd kafka3.0.1/
# 修改zookeeper配置文件
vim ./config/zookeeper.properties
# 修改数据持久化存储路径
dataDir=/opt/tmp/zookeeper
# 保存并退出
启动zk
./bin/zookeeper-server-start.sh ./config/zookeeper.properties
- 启动kafka
修改kafka配置文件
vim ./config/server0.properties
# 搭建集群时的唯一标识
broker.id=0
# 部署的机器 IP 和对外提供服务的端口, 默认9092
#listeners=PLAINTEXT://:9092
# 修改数据文件路径
log.dirs=/opt/kafka/kafka-logs
# 连接 zookeeper
zookeeper.connect=192.168.10.100:2181
# 保存并退出
启动
./bin/kafka-server-start.sh -daemon ./config/server.properties
- 客户端登录zk, 查看节点信息
./bin/zookeeper-shell.sh 47.98.100.76:2181 ls /
# 查看kafka节点
./bin/zookeeper-shell.sh 47.98.100.76:2181 ls /brokers/ids/0
实现
conf 为配置文件目录
- config.ini
[kafka]
addr=192.168.100.76:9092
topic=chao2022
[tail]
filename=./log/my.log
- config.go
配置文件结构体, 将配置文件信息映射到该结构体, 方便获取值
package conf
type Cfg struct {
KafkaCfg `ini:"kafka"`
TailCfg `ini:"tail"`
}
type KafkaCfg struct {
Addr string `ini:"addr"`
Topic string `ini:"topic"`
}
type TailCfg struct {
Filename string `ini:"filename"`
}
kafka 专门往kafka写数据的模块
- kafka.go
package kafka
import (
"fmt"
"github.com/Shopify/sarama"
)
var (
producer sarama.SyncProducer
err error
)
// Init 初始化 client
func Init(addr []string) error {
config := sarama.NewConfig()
config.Producer.RequiredAcks = sarama.WaitForAll
config.Producer.Partitioner = sarama.NewHashPartitioner // 设置选择分区的策略为Hash
config.Producer.Return.Successes = true // 成功交付的消息将在success channel返回
// 生产者
producer, err = sarama.NewSyncProducer(addr, config)
if err != nil {
return err
}
return nil
}
func SendMsg(topic, key, value string) error {
// 构造一个消息
msg := &sarama.ProducerMessage{}
msg.Topic = topic // 指定主题Topic
msg.Value = sarama.StringEncoder(value) // 消息内容
msg.Key = sarama.StringEncoder(key) // 设置key
// 分区ID, 偏移量
pid, offset, err := producer.SendMessage(msg)
if err != nil {
fmt.Println("send msg failed, err:", err)
return err
}
fmt.Printf("pid:%v offset:%v\n", pid, offset)
return nil
}
producer设置ack参数
- WaitForLocal: 消息同步到master之后返回ack信号,否则抛异常使应用程序感知到并在业务中进行重试发送。这种方式一定程度保证了消息的可靠性,producer等待broker确认信号的时延也不高。
- WaitForAll: 消息同步到master且同步到所有follower之后返回ack信号,否则抛异常使应用程序感知到并在业务中进行重试发送。这样设置,在更大程度上保证了消息的可靠性,缺点是producer等待broker确认信号的时延比较高。
订阅kafka的消费者如何按照消息顺序写入mysql, Kafka的消息在一个partition中是有序的,所以只要确保发给某个人的消息都在同一个partition中即可
- 全局一个partition, 这个最简单,但是在kafka中一个partition对应一个线程,所以这种模型下Kafka的吞吐是个问题。
- 多个partition手动指定, 生产消息的时候,除了Topic和Value,我们可以通过手动指定partition,比如总共有10个分区,我们根据用户ID取余,这样发给同一个用户的消息,每次都到1个partition里面去了,消费者写入mysql中的时候,自然也是有序的。但是,因为分区总数是写死的,万一Kafka的分区数要调整呢?那不得重新编译代码?所以这个方式不够优美。
- 多个partition自动计算, kafka客户端为我们提供了这种支持。在初始化的时候,设置选择分区的策略为Hash, 然后在生成消息之前,设置消息的Key值, Kafka客户端会根据Key进行Hash,我们通过把接收用户ID作为Key,这样就能让所有发给某个人的消息落到同一个分区了,也就有序了。
tail 读取日志文件的模块
package tail
import (
"github.com/hpcloud/tail"
)
var (
tailObj \*tail.Tail
)
// Init 初始化
func Init(path string) error {
cfg := tail.Config{
ReOpen: true, // 重新打开, 在单个日志文件写满做切隔时, 重新打开新一个文件
Follow: true, // 是否跟随
Location: &tail.SeekInfo{ // 从文件的哪个位置开始读
Offset: 0,
Whence: 2,
},
MustExist: false,
Poll: true,
}
t, err := tail.TailFile(path, cfg)
if err != nil {
return err
}
tailObj = t
return nil
}
func Read() chan \*tail.Line{
return tailObj.Lines
}
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!