kafka
见:https://www.cnblogs.com/yangxiaoyi/p/7359236.html
kafka是一种高吞吐量的分布式发布订阅消息系统。
消息模型可以分为两种:队列和发布-订阅式。队列的处理方式是一组消费者从服务器读取消息,一条消息只有其中的一个消费者来处理。
主题和日志 (Topic和Log)。每一个分区(partition)都是一个顺序的、不可变的消息队列,并且可以持续的添加。分区中的消息都被分了一个序列号,称之为偏移量(offset),在每个分区中此偏移量都是唯一的。Kafka集群保持所有的消息,直到它们过期,无论消息是否被消费了。实际上消费者所持有的仅有的元数据就是这个偏移量,也就是消费者在这个log中的位置。 这个偏移量由消费者控制:正常情况当消费者消费消息的时候,偏移量也线性的的增加。但是实际偏移量由消费者控制,消费者可以将偏移量重置为更老的一个偏移量,重新读取消息。 可以看到这种设计对消费者来说操作自如, 一个消费者的操作不会影响其它消费者对此log的处理。
zookpeer
见:https://www.cnblogs.com/felixzh/p/5869212.html
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户
见:https://www.cnblogs.com/leaf-7/p/5310054.html
Kafka中ZooKeeper的用途:正如ZooKeeper用于分布式系统的协调和促进,Kafka使用ZooKeeper也是基于相同的原因。ZooKeeper用于管理、协调Kafka代理。每个Kafka代理都通过ZooKeeper协调其它Kafka代理。当Kafka系统中新增了代理或者某个代理故障失效时,ZooKeeper服务将通知生产者和消费者。生产者和消费者据此开始与其它代理协调工作。
见:https://blog.csdn.net/eric_sunah/article/details/44201711
Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
"code.byted.org/golf/ssconf"
"github.com/Shopify/sarama"
raven "github.com/getsentry/raven-go"
"github.com/sirupsen/logrus"
"github.com/wvanbergen/kafka/consumergroup"
kazoo "github.com/wvanbergen/kazoo-go"
)
const (
KafkaConfFile = "/aa/bb/kafka.conf"
)
type Queue struct {
Consumer *consumergroup.ConsumerGroup
process func(*sarama.ConsumerMessage)
customizeFunc func(*sarama.ConsumerMessage) error
}
func Init(clusterName, consumerGroup string, topicNames []string, commitInterval time.Duration) *Queue {
config := consumergroup.NewConfig()
config.Offsets.Initial = sarama.OffsetOldest
config.Offsets.ProcessingTimeout = 10 * time.Second
config.Offsets.ResetOffsets = false
config.Offsets.CommitInterval = commitInterval
// 获得所有 kafka 集群名
confFile, err := ssconf.GetTotalConfigure(KafkaConfFile)
if err != nil {
raven.CaptureError(err, nil)
logrus.Errorf("Start consume kafka wifi log failed: %s", err.Error())
}
// 寻找特定集群名
// 得到 zookeeper 所有节点名,根目录
zookeeper := confFile[fmt.Sprintf("%s_zookeeper", clusterName)]
var zookeeperNodes []string
zookeeperNodes, config.Zookeeper.Chroot = kazoo.ParseConnectionString(zookeeper)
// 使用 zookeeper 发现 consumer 群后加入
consumer, err := consumergroup.JoinConsumerGroup(consumerGroup, topicNames, zookeeperNodes, config)
if err != nil {
panic(err)
}
// 如果有收到终止信号,停止 consumer 群
CloseChan := make(chan os.Signal, 1)
signal.Notify(CloseChan, syscall.SIGTERM)
go func() {
<-CloseChan
if err := consumer.Close(); err != nil {
sarama.Logger.Println("Error closing the consumer", err)
}
}()
// 收集出错日志
go func() {
for err := range consumer.Errors() {
raven.CaptureError(err, nil)
logrus.Errorf("Consume kafka wifi log failed: %s", err.Error())
}
}()
// 创建 consumer 队列
queue := &Queue{
Consumer: consumer,
}
if commitInterval != 0 {
// 调用 consumer 的自定义消息处理函数
queue.process = func(message *sarama.ConsumerMessage) {
if err := queue.customizeFunc(message); err != nil {
raven.CaptureError(err, nil)
logrus.Errorf("Queue Run Event error: err= %s message= %+v value= %s", err, message, string(message.Value))
} else {
// 告诉 offset manager,consumer 已经成功处理了消息
queue.Consumer.CommitUpto(message)
}
}
} else {
queue.process = func(message *sarama.ConsumerMessage) {
if err := queue.customizeFunc(message); err != nil {
raven.CaptureError(err, nil)
logrus.Errorf("Queue Run Event error: err= %s message= %+v value= %s", err, message, string(message.Value))
}
}
}
return queue
}
func (queue *Queue) Run(f func(*sarama.ConsumerMessage) error) {
queue.customizeFunc = f
go queue.run()
}
func (queue *Queue) run() {
for m := range queue.Consumer.Messages() {
go raven.CapturePanic(func() {
queue.process(m)
}, nil)
}
}
func (queue *Queue) Commit(m *sarama.ConsumerMessage) error {
return queue.Consumer.CommitUpto(m)
}
func (queue *Queue) Flush() error {
return queue.Consumer.FlushOffsets()
}
=========================================================
const (
clusterName = "aa"
logConsumerGroup = "bb"
eventConsumerGroup = "cc"
)
var (
logTopicNames = []string{"mylogs"}
)
// 初始化
func queueInit() {
logQueue := Init(clusterName, logConsumerGroup, logTopicNames, time.Second*10)
logQueue.Run(func(message *sarama.ConsumerMessage) error {
return myLogFunc(message.Value)
})
}