一、项目目录
jocko源码中直接使用了开源serf和raft库,整个项目的代码非常简洁,其项目目录如下:
├── _examples
│ ├── cluster
│ └── sarama
├── cmd
│ └── jocko
├── commitlog
├── jocko
│ ├── config
│ ├── fsm
│ ├── metadata
│ ├── state
│ ├── structs
│ └── util
├── log
├── mock
├── protocol
└── testutil
二、阅读代码切入点
这里,选择example作为阅读的切入点,通过查看如何使用来了解整个核心流程。
1、单节点的场景-example/sarama
该例子展示了如何使用Sarama与Jocko协同生产和消费者消息。
其中Sarama 是一个用 Go 语言编写的 Apache Kafka 客户端库,它允许开发者在 Go 应用程序中轻松地与 Kafka 集群进行交互,包括发布消息到 Kafka 主题(生产者角色)、订阅并消费主题中的消息(消费者角色)以及管理 Kafka 集群的元数据。
下面是项目中给出的例子,这里将一些校验和输出的代码删除,仅留下主要逻辑:
1)首先是创建kafka server,即在集群中创建一个broker
func setup() (*jocko.Server, func()) {
// 创建broker
c, cancel := jocko.NewTestServer(&testing.T{}, func(cfg *config.Config) {
cfg.Bootstrap = true
cfg.BootstrapExpect = 1
cfg.StartAsLeader = true
}, nil)
// 开启server服务
if err := c.Start(context.Background()); err != nil {
fmt.Fprintf(os.Stderr, "failed to start cluster: %v\n", err)
os.Exit(1)
}
//创建borker的连接
conn, err := jocko.Dial("tcp", c.Addr().String())
if err != nil {
fmt.Fprintf(os.Stderr, "error connecting to broker: %v\n", err)
os.Exit(1)
}
//通过该连接创建topic、topic中的分区数量以及对应的副本个数
resp, err := conn.CreateTopics(&protocol.CreateTopicRequests{
Requests: []*protocol.CreateTopicRequest{{
Topic: topic,
NumPartitions: numPartitions,
ReplicationFactor: 1,
}},
})
if err != nil {
fmt.Fprintf(os.Stderr, "failed with request to broker: %v\n", err)
os.Exit(1)
}
...
return c, func() {
cancel()
c.Shutdown()
os.RemoveAll(logDir)
}
}
2)连接kafka集群,由sarama根据提供的broker ip信息与Kafka集群建立网络连接
brokers := []string{s.Addr().String()}
producer, err := sarama.NewSyncProducer(brokers, config)
if err != nil {
panic(err)
}
3)往创建的topic中生产数据
for i := 0; i < messageCount; i++ {
message := fmt.Sprintf("Hello from Jocko #%d!", i)
partition, offset, err := producer.SendMessage(&sarama.ProducerMessage{
Topic: topic,
Value: sarama.StringEncoder(message),
}) // 指定发送的数据所在topic
if err != nil {
panic(err)
}
pmap[partition] = append(pmap[partition], check{
partition: partition,
offset: offset,
message: message,
})
}
if err = producer.Close(); err != nil {
panic(err)
}
4)从创建的topic中消费数据
for partitionID := range pmap {
consumer, err := sarama.NewConsumer(brokers, config)
if err != nil {
panic(err)
}
partition, err := consumer.ConsumePartition(topic, partitionID, 0)
if err != nil {
panic(err)
}
for msg := range partition.Messages() {
... ...// 对读取到的信息进行校验
}
}
2、集群场景-examples/cluster
使用命令行启动集群
2.1 命令介绍
代码位置:jocko/main.go:
-
主命令broker,它定义了启动 Jocko broker的行为,下面是它的标志(Flags):
--raft-addr:Raft 协议绑定和对外宣告的地址,默认为 "127.0.0.1:9093"。--data-dir:存储日志文件的目录,默认为 "/tmp/jocko",可以是逗号分隔的多个目录。--broker-addr:broker 绑定的地址,默认为 "0.0.0.0:9092"。--serf-addr:Serf 服务发现组件绑定的地址,通过一个newMemberlistConfigValue函数封装参数,初始默认值为 "0.0.0.0:9094"。--bootstrap:标识是否进行初始集群引导(危险操作)。--bootstrap-expect:预期集群中的节点数。--join和--join-wan:启动时加入的其他 broker 节点的 Serf LAN 和 WAN 地址,可以多次指定。--id:broker 的 ID。
-
主命令topic,作为管理主题的的命令,存在一个子命令create,该子命令存在以下标志位:
--broker-addr:与 broker 交互时需要绑定的地址,默认为 "0.0.0.0:9092"。--topic:待创建主题的名字,这是必填项,通过MarkFlagRequired("topic")标记为必需。--partitions:主题的分区数量,默认为 1。--replication-factor:主题的复制因子,默认为 1
2.2 命令启动集群例子
代码 example/cluster/README.md
$ ./jocko broker \
--data-dir="/tmp/jocko0" \
--broker-addr=127.0.0.1:9001 \
--raft-addr=127.0.0.1:9002 \
--serf-addr=127.0.0.1:9003 \
--bootstrap \
--bootstrap-expect=3 \
--id=1
$ ./jocko broker \
--data-dir="/tmp/jocko1" \
--broker-addr=127.0.0.1:9101 \
--raft-addr=127.0.0.1:9102 \
--serf-addr=127.0.0.1:9103 \
--join=127.0.0.1:9003 \
--bootstrap-expect=3 \
--id=2
$ ./jocko broker \
--data-dir="/tmp/jocko2" \
--broker-addr=127.0.0.1:9201 \
--raft-addr=127.0.0.1:9202 \
--serf-addr=127.0.0.1:9203 \
--join=127.0.0.1:9003 \
--bootstrap-expect=3 \
--id=3
其中启动的第一个节点是初始集群的引导节点,下面两个节点是加入该集群中的,他们都指向第一个节点即引导节点来加入,同时每个节点都存在一个编号。