需要借助的库

github.com/Shopify/sarama // kafka主要的库*
github.com/bsm/sarama-cluster // kafka消费组

生产者

package producer

import (
"fmt"
"github.com/HappyTeemo7569/teemoKit/tlog"
"github.com/Shopify/sarama"
"kafkaDemo/define"
) var (
ProducerId = 1
) type Producer struct {
Producer sarama.SyncProducer
Topic string //主题
ProducerID int //生产者Id
MessageId int
} func (p *Producer) InitProducer() {
config := sarama.NewConfig()
config.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认
config.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition
config.Producer.Return.Successes = true // 成功交付的消息将在success channel返回 // 连接kafka
client, err := sarama.NewSyncProducer([]string{define.SERVER_LIST}, config)
if err != nil {
tlog.Error("producer closed, err:", err)
return
} p.Producer = client
p.Topic = define.TOPIC
p.ProducerID = ProducerId
p.MessageId = 1 ProducerId++
} func (p *Producer) SendMessage() {
// 构造一个消息
msg := &sarama.ProducerMessage{}
msg.Topic = p.Topic
txt := fmt.Sprintf("ProducerID:%d this is a test log %d",
p.ProducerID, p.MessageId)
msg.Value = sarama.StringEncoder(txt) // 发送消息
pid, offset, err := p.Producer.SendMessage(msg)
//_, _, err := client.SendMessage(msg)
if err != nil {
fmt.Println("send msg failed, err:", err)
return
}
tlog.Info(fmt.Sprintf("ProducerID:%d pid:%v offset:%v msg:%s",
p.ProducerID, pid, offset, txt)) p.MessageId++
} func (p *Producer) Close() {
p.Producer.Close()
}

消费者

package consumer

import (
"github.com/HappyTeemo7569/teemoKit/tlog"
"github.com/Shopify/sarama"
"kafkaDemo/define"
) type Consumer struct {
Consumer sarama.Consumer
Topic string
ConsumerId int //消费者Id
} func (c *Consumer) InitConsumer() error {
consumer, err := sarama.NewConsumer([]string{define.SERVER_LIST}, nil)
if err != nil {
return err
}
c.Consumer = consumer
c.Topic = define.TOPIC
c.ConsumerId = ConsumerId
ConsumerId++
return nil
} //指定partition
//offset 可以指定,传-1为获取最新offest
func (c *Consumer) GetMessage(partitionId int32, offset int64) {
if offset == -1 {
offset = sarama.OffsetNewest
}
pc, err := c.Consumer.ConsumePartition(c.Topic, partitionId, offset)
if err != nil {
tlog.Error("failed to start consumer for partition %d,err:%v", partitionId, err)
//That topic/partition is already being consumed
return
} // 异步从每个分区消费信息
go func(sarama.PartitionConsumer) {
for msg := range pc.Messages() {
tlog.Info("ConsumerId:%d Partition:%d Offset:%d Key:%v Value:%v", c.ConsumerId, msg.Partition, msg.Offset, msg.Key, string(msg.Value))
}
}(pc)
} //遍历所有分区
func (c *Consumer) GetMessageToAll(offset int64) {
partitionList, err := c.Consumer.Partitions(c.Topic) // 根据topic取到所有的分区
if err != nil {
tlog.Error("fail to get list of partition:err%v", err)
return
}
tlog.Info("所有partition:", partitionList) for partition := range partitionList { // 遍历所有的分区
c.GetMessage(int32(partition), offset)
}
}

主函数

func main() {
tlog.Info("开始") go producer.Put()
go consumer.Get() for {
time.Sleep(time.Hour * 60)
}
} func Put() {
producer := new(Producer)
producer.InitProducer()
go func() {
for {
producer.SendMessage()
time.Sleep(1 * time.Second)
}
}()
} func Get() { offest := int64(0) consumer := new(Consumer)
err := consumer.InitConsumer()
if err != nil {
tlog.Error("fail to init consumer, err:%v", err)
return
}
consumer.GetMessageToAll(offest)
}

具体源码可以查看:

kafka_demo

生产环境中的优化

  • 可以存储消费的节点到redis
  • 需要顺序的消费的放到一个partition,或者利用哈希算法投递
  • 传入一个通道,将业务逻辑和底层逻辑解耦。
package kafka

import (
"crypto/tls"
"crypto/x509"
"fmt"
"github.com/Shopify/sarama"
"io/ioutil"
"log"
"sync"
"vliao.com/stellar/internal/core"
) type KafkaConsumer struct {
Node []string
Consumer sarama.Consumer
Topic string
MessageQueue chan []byte
} func NewKafkaConsumer(topic string) KafkaConsumer {
return KafkaConsumer{
Node: core.GetKafkaConn().Conn,
Topic: core.GetServerMode() + "_" + topic,
}
} // Consume 获取所有分区
func (c *KafkaConsumer) Consume() {
config := sarama.NewConfig() config.Net.SASL.Enable = true
config.Net.SASL.User = core.GetKafkaConn().SASLUser
config.Net.SASL.Password = core.GetKafkaConn().SASLPassword
config.Net.SASL.Handshake = true certBytes, err := ioutil.ReadFile(GetFullPath("only-4096-ca-cert"))
if err != nil {
fmt.Println("kafka client read cert file failed ", err.Error())
return
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
fmt.Println("kafka client failed to parse root certificate")
return
}
config.Net.TLS.Config = &tls.Config{
RootCAs: clientCertPool,
InsecureSkipVerify: true,
}
config.Net.TLS.Enable = true consumer, err := sarama.NewConsumer(c.Node, config)
if err != nil {
log.Fatal("NewConsumer err: ", err)
}
defer consumer.Close() // 先查询该 topic 有多少分区
partitions, err := consumer.Partitions(c.Topic)
if err != nil {
log.Fatal("Partitions err: ", err)
}
var wg sync.WaitGroup
wg.Add(len(partitions))
// 然后每个分区开一个 goroutine 来消费
for _, partitionId := range partitions {
//不开异步会导致一个消费完才会消费另外一个
go c.consumeByPartition(consumer, c.Topic, partitionId, &wg)
}
wg.Wait()
} // 暂时只是业务一对一,也就是一个生产者产生的消息不会触发多个业务的变动
// 但是可以开多个消费者增加处理能力
func getOffsetCacheKey(topic string, partitionId int32) string {
return fmt.Sprintf("kafka_offset_%s_%d", topic, partitionId)
} func setConsumeOffset(topic string, partitionId int32, offset int64) {
core.RedisBy(core.RedisTypeServer).SetInt64(getOffsetCacheKey(topic, partitionId), offset)
}
func getConsumeOffset(topic string, partitionId int32) (offset int64) {
key := getOffsetCacheKey(topic, partitionId)
if core.RedisBy(core.RedisTypeServer).Exists(key) {
return core.RedisBy(core.RedisTypeServer).GetInt64(key) + 1
} //默认从最新开始
setConsumeOffset(topic, partitionId, sarama.OffsetNewest)
return sarama.OffsetNewest
} func (c *KafkaConsumer) consumeByPartition(consumer sarama.Consumer, topic string, partitionId int32, wg *sync.WaitGroup) {
defer wg.Done()
offset := getConsumeOffset(topic, partitionId)
partitionConsumer, err := consumer.ConsumePartition(topic, partitionId, offset)
if err != nil {
log.Fatal("ConsumePartition err: ", err)
}
defer partitionConsumer.Close()
for message := range partitionConsumer.Messages() {
log.Printf("[Consumer] topic: %s ; partitionid: %d; offset:%d, value: %s\n", topic, message.Partition, message.Offset, string(message.Value))
setConsumeOffset(topic, partitionId, message.Offset)
c.MessageQueue <- message.Value
}
}
package kafka

import (
"log"
"testing"
"vliao.com/stellar/internal/core"
) func Test_Get(t *testing.T) {
core.TestMain()
topic := "test_log"
var kafkaConsumer = NewKafkaConsumer(topic) kafkaConsumer.MessageQueue = make(chan []byte, 1000)
go kafkaConsumer.Consume() for {
msg := <-kafkaConsumer.MessageQueue
deal(msg)
} } func deal(msg []byte) {
log.Printf(string(msg))
}

Go接入kafka的更多相关文章

  1. 【kafka学习笔记】PHP接入kafka

    安装扩展 # 先安装rdkfka库文件 git clone https://github.com/edenhill/librdkafka.git 或者: wget https://gitee.com/ ...

  2. kafka和mqtt的区别是什么?

    两者都是从传统的Pub/Sub消息系统演化出来的,但是进化方向不一样,比较如下: Kafka是为了数据集成的场景,与以往Pub/Sub消息总线不一样,通过分布式架构提供了海量消息处理.高容错的方式存储 ...

  3. 微服务日志之.NET Core使用NLog通过Kafka实现日志收集

    一.前言 NET Core越来越受欢迎,因为它具有在多个平台上运行的原始.NET Framework的强大功能.Kafka正迅速成为软件行业的标准消息传递技术.这篇文章简单介绍了如何使用.NET(Co ...

  4. ELK + kafka 日志方案

    概述 本文介绍使用ELK(elasticsearch.logstash.kibana) + kafka来搭建一个日志系统.主要演示使用spring aop进行日志收集,然后通过kafka将日志发送给l ...

  5. kafka 集群的部署安装

    这里我们罗列一下我们的环境 10.19.18.88 zk1 10.19.16.84 zk2 10.19.11.44 zk3 这里公司需要接入kafka用于zipkin来定位调用链 kafka 的地址是 ...

  6. .NET Core使用NLog通过Kafka实现日志收集

    微服务日志之.NET Core使用NLog通过Kafka实现日志收集 https://www.cnblogs.com/maxzhang1985/p/9522017.html 一.前言 NET Core ...

  7. spark streaming 接收kafka消息之五 -- spark streaming 和 kafka 的对接总结

    Spark streaming 和kafka 处理确保消息不丢失的总结 接入kafka 我们前面的1到4 都在说 spark streaming 接入 kafka 消息的事情.讲了两种接入方式,以及s ...

  8. ELK + kafka 分布式日志解决方案

    概述 本文介绍使用ELK(elasticsearch.logstash.kibana) + kafka来搭建一个日志系统.主要演示使用spring aop进行日志收集,然后通过kafka将日志发送给l ...

  9. kafka中的配额管理(限速)机制

    kafka支持配额管理,从而可以对Producer和Consumer的produce&fetch操作进行流量限制,防止个别业务压爆服务器.本文主要介绍如何使用kafka的配额管理功能. 1 K ...

  10. 在kubernetes上部署zookeeper,kafka集群

    本文采用网上镜像:mirrorgooglecontainers/kubernetes-zookeeper:1.0-3.4.10 准备共享存储:nfs,glusterfs,seaweed或其他,并在no ...

随机推荐

  1. 显式等待(一)WebDriverWait类、until()方法

    转自: https://blog.csdn.net/zyooooxie/article/details/84422924

  2. app内打开外部第三方瞎下载链接

    Q:我用cordova开发项目,想在app内跳转外部链接,安装了cordova-plugin-inappbrowser后确实可以跳转,但是跳转的页面有个按钮,原本点击会下载app,现在点击后毫无反应, ...

  3. C#TimeSpan时间差转换成分钟和秒数

    public Form1() { InitializeComponent(); aa = DateTime.Now.ToString(); } string aa; private void butt ...

  4. Java基础|01.基础语法(1)

    目录 00x1 基本语法 1.类的语法 2.对象的语法 3.方法的声明 4.小例子 00x2 类和对象的关系 1.堆.栈和元空间 2.基本数据类型和引用数据类型的区别 3. 空对象(null) 00x ...

  5. font-awesome vue/react 通用的图标

    在开发项目中遇到了矢量图标 一套绝佳的图标字体库和CSS框架 vue 中引入font-awesome 直接npm install font-awesome --save 就可以了,里边包含了样式和字体 ...

  6. 在NCBI中下载SRA数据

    目前,在NCBI中下载SRA数据主要有三种方式: 利用Aspera工具下载. 利用SRA Toolkit下载. 利用wget命令直接下载 第三种最为方便.其中的关键是得到下载数据的链接,即ftp的地址 ...

  7. dpkt 简单应用

    import dpktfrom dpkt.utils import mac_to_str,inet_to_strcap=f'D:/test_pacp/6.pcap'with open(cap,'rb' ...

  8. 鸿蒙hi3861V100开发板问题记录

    1.引脚复用 2.引脚复用方法: 1.看业务代码使用的是uart几,如使用的是uart2(实测可用uart1 tx为GPIO6, rx为GPIO5:uart2 tx为GPIO11,rx为GPIO12) ...

  9. ssh问题、原理及diffie hellman算法

    1.普通用户无法使用证书登录:原因是权限设置问题 将.ssh目录设为700,authorized_keys设为600即可. 2.查看ssh支持的算法 ssh -Q help ssh -Q kex/ke ...

  10. 构建 Maven 项目卡爆?优化后:1 秒完成…

    在实际开发中,我们通常会用到maven的archetype插件(原型框架)来生成项目框架. 但是无奈,创建时,总会卡在: [INFO] Generating project in Batch mode ...