需要借助的库

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. 在Unity3D中开发的坦克履带模拟器Tank Track Simulator

    为了在Unity游戏中比较真实地模拟坦克履带的运动,本人便开发了这款Tank Track Simulator插件 特点 比较真实地模拟了坦克履带的运动. 本插件中包含了一辆M1A2坦克模型,已经将这个 ...

  2. 在Unity3D中开发的Sketch Shader

    Pencil Sketch Shader 特点 此素描渲染风格的Shader是顶点片元Shader,由本人手动编写完成. 此素描渲染风格的Shader已经在移动设备真机上进行过测试,可以直接应用到您的 ...

  3. 招新题流程简介(WS2812)

    22物电科协软件招新题学习流程 有错误或者不当的地方请在评论区指出 题目简介 使用stm32驱动单一ws2812b灯珠实现呼吸灯效果,驱动及实现方法不限 演示效果 快速入门,在stm32核心板上点灯 ...

  4. json字符串让里面的""和null不显示出来

    工具类 ------------------------------------------------------------------------------------------------ ...

  5. Calendar设定月份时要注意日期

    先看下代码 public static void main(String[] args) { int dataMonth = 4; DateFormat dateFormat = new Simple ...

  6. Think in UML 其二

    UML基本元素 参与者 1.参与者位于系统边界之外. 思考参与者究竟是谁时,以下两个问题有助于了解 ·谁对系统有着明确的目标和要求并且主动发出动作? ·系统是为谁服务的? 2.参与者可以非人 功能性需 ...

  7. 实验8 python库应用编程与体验

    3.实验任务3 from turtle import * def square(size=50, rgb='orange'): '''绘制正方形 参数size指定边长 参数rgb指定画笔颜色 如果没有 ...

  8. 如何解决7z: command not found问题

    7z是一种常见的压缩文件格式,如果你想要压缩或解压缩7z文件,你需要在你的系统上安装p7zip和p7zip-full.但是,有时候你会发现当你尝试运行7z或7za命令时,它会显示"bash: ...

  9. 时序图,E-R图,数据流程图

    其中OSC是在线客服     数据流程图

  10. uc 小游戏接入经验

    使用引擎,egret 相关资料: https://blog.csdn.net/weixin_42276579/article/details/107379544 https://minigame.uc ...