既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
启动
./bin/kafka-server-start.sh -daemon ./config/server.properties
5. 客户端登录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
#### 实现
![在这里插入图片描述](https://img-blog.csdnimg.cn/fa86344fbdae47d2a63a27b7f03e941a.png)
###### conf 为配置文件目录
1. config.ini
[kafka]
addr=192.168.100.76:9092
topic=chao2022
[tail]
filename=./log/my.log
2. 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写数据的模块
1. 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
}
###### main
package main
import (
“fmt”
“logagent/conf”
“logagent/kafka”
“logagent/tail”
“gopkg.in/ini.v1”
“time”
)
var (
cfg conf.Cfg
err error
)
func main() {
// 0. 初始化配置文件
err = ini.MapTo(&cfg, “./conf/config.ini”)
if err != nil {
fmt.Println("init config failed: ", err.Error())
return
}
// 1. 初始化kafka连接
addr := []string{cfg.KafkaCfg.Addr}
err = kafka.Init(addr)
if err != nil {
fmt.Println("init kafka failed: ", err.Error())
return
}
fmt.Println("init kafka success.")
// 2. 打开日志文件读取
filename := cfg.TailCfg.Filename
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!