go资深之路笔记(九)kafka浅析

一、作用

kafka 是一个消息队列,但不止是一个消息队列,他是一个分布式流式平台;

其实 go可选择的消息队列工具不止一个他们的适用场景各有不同,如果需要高吞吐量的话可以优先考虑,下面是ai给出的讲解:

二、confluent-kafka-go 客户端:

go的客户端库有多个,这里只讲 confluent-kafka-go

首先说下 他的api:

go 复制代码
// 生产者api
func NewProducer(conf *ConfigMap) (*Producer, error) // 创建
func (p *Producer) Produce(msg *Message, deliveryChan chan Event) error // 发送消息
func (p *Producer) Events() chan Event // 获取生产者事件(比如发送完成后回调)
func (p *Producer) Flush(timeoutMs int) int // 确保所有消息发送完成

// 消费者api
func NewConsumer(conf *ConfigMap) (*Consumer, error) 	// 创建
func (c *Consumer) SubscribeTopics(topics []string, rebalanceCb RebalanceCb) (err error)	// 订阅主题
func (c *Consumer) Poll(timeoutMs int) (event Event) 	// 拉取信息
func (c *Consumer) CommitMessage(m *Message) ([]TopicPartition, error)	//提交偏移量(告诉服务端消息已经处理了,这个最好手动,不然默认提交的话失败了就丢失了)

// 结构体
type ConfigMap map[string]ConfigValue			// 配置

type Message struct { // 消息
	TopicPartition TopicPartition
	Value          []byte
	Key            []byte
	Timestamp      time.Time
	TimestampType  TimestampType
	Opaque         interface{}
	Headers        []Header
}

type TopicPartition struct {		// 主题分区信息
	Topic     *string
	Partition int32
	Offset    Offset
	Metadata  *string
	Error     error
}

三、实战:

3.1 docker kafka服务器创建

docker-compose.yml

go 复制代码
	version: '3'
    services:
      zookeeper:
        image: wurstmeister/zookeeper
        ports:
          - "2181:2181"

      kafka:
        image: wurstmeister/kafka
        ports:
          - "9092:9092"
        environment:
          KAFKA_ADVERTISED_HOST_NAME: localhost
          KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
          KAFKA_CREATE_TOPICS: "test_topic:1:1"
        depends_on:
          - zookeeper

3.2 启动

bash 复制代码
 docker-compose up -d

3.3 修改配置:

ps:如果服务器和客户端都在本地运行不用改也行

bash 复制代码
# 进入容器内部
 docker-compose exec kafka bash

# 看 kafka的配置在哪个目录
  ls -l /etc/kafka/
  ls -l /opt/kafka/
 # 我的在 /opt/kafka/  | 打开 server.properties配置
 vim /opt/kafka/config/server.properties
 # 修改配置参数:
 listeners=PLAINTEXT://0.0.0.0:9092
 advertised.listeners=PLAINTEXT://你的公网IP:9092
 #保存退出
 ## 重启生效:
 docker-compose restart kafka 

3.4 生产者代码

confluent-kafka.go

go 复制代码
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/confluentinc/confluent-kafka-go/kafka"
)

func main() {
	// 生产者配置 :cite[4]
	config := &kafka.ConfigMap{
		"bootstrap.servers": "配置填写的ip:9092", // Kafka broker地址  !!! todo 注意改成自己的ip
		// 如需SASL认证(如连接云服务),取消下面注释 :cite[4]:cite[6]
		// "security.protocol": "SASL_PLAINTEXT",
		// "sasl.mechanism":    "PLAIN",
		// "sasl.username":     "your_username",
		// "sasl.password":     "your_password",
		"acks":               -1,   // 等待所有副本确认,保证数据不丢失
		"retries":            3,    // 失败重试次数
		"retry.backoff.ms":   1000, // 重试间隔
		"enable.idempotence": true, // 启用幂等性,避免重复消息
	}

	// 创建生产者
	producer, err := kafka.NewProducer(config)
	if err != nil {
		log.Fatalf("Error creating producer: %v", err)
	}
	defer producer.Close()

	// 监听消息传递结果
	go func() {
		for e := range producer.Events() {
			switch ev := e.(type) {
			case *kafka.Message:
				if ev.TopicPartition.Error != nil {
					fmt.Printf("Delivery failed: %v\n", ev.TopicPartition)
				} else {
					fmt.Printf("Delivered message to %v, key: %s\n",
						ev.TopicPartition, string(ev.Key))
				}
			}
		}
	}()

	// 发送消息
	topic := "test_topic"
	for i := 0; i < 10; i++ {
		key := fmt.Sprintf("key-%d", i)
		value := fmt.Sprintf("Hello Kafka! %s", time.Now().Format("15:04:05"))

		err = producer.Produce(&kafka.Message{
			TopicPartition: kafka.TopicPartition{
				Topic:     &topic,
				Partition: kafka.PartitionAny,
			},
			Key:   []byte(key),
			Value: []byte(value),
		}, nil)

		if err != nil {
			log.Printf("Produce error: %v", err)
		}

		time.Sleep(time.Second)
	}

	// 等待所有消息发送完成
	producer.Flush(15 * 1000)
	fmt.Println("All messages sent")
}

运行后:

3.5 消费者代码

go 复制代码
package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/confluentinc/confluent-kafka-go/kafka"
)

func main() {
	// 消费者配置 :cite[6]
	config := &kafka.ConfigMap{
		"bootstrap.servers": "配置填写的ip:9092", // Kafka broker地址  !!! todo 注意改成自己的ip
		// 如需SASL认证(如连接云服务),取消下面注释 :cite[4]:cite[6]
		// "security.protocol": "SASL_SSL",
		// "sasl.mechanism":    "PLAIN",
		// "sasl.username":     "your_username",
		// "sasl.password":     "your_password",
		"group.id":                 "test_consumer_group", // 消费组ID
		"auto.offset.reset":        "earliest",            // 从最早的消息开始消费
		"enable.auto.commit":       false,                 // 手动提交偏移量
		"enable.auto.offset.store": false,                 // 手动存储偏移量
		"max.poll.interval.ms":     300000,                // 最大poll间隔
		"session.timeout.ms":       10000,                 // 会话超时
	}

	// 创建消费者
	consumer, err := kafka.NewConsumer(config)
	if err != nil {
		log.Fatalf("Error creating consumer: %v", err)
	}
	defer consumer.Close()

	// 订阅主题
	err = consumer.SubscribeTopics([]string{"test_topic"}, nil)
	if err != nil {
		log.Fatalf("Subscribe error: %v", err)
	}

	// 处理中断信号
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)

	// 消费消息
	run := true
	for run {
		select {
		case sig := <-sigchan:
			fmt.Printf("Caught signal %v: terminating\n", sig)
			run = false
		default:
			// 轮询消息,超时时间100ms
			event := consumer.Poll(100)
			if event == nil {
				continue
			}

			switch e := event.(type) {
			case *kafka.Message:
				fmt.Printf("Received message: %s (key: %s) [%s] at offset %v\n",
					string(e.Value), string(e.Key), e.TopicPartition, e.TopicPartition.Offset)

				// 处理业务逻辑...

				// 手动提交偏移量
				_, err := consumer.CommitMessage(e)
				if err != nil {
					fmt.Printf("Commit error: %v\n", err)
				}
			case kafka.Error:
				fmt.Printf("Error: %v\n", e)
				if e.Code() == kafka.ErrAllBrokersDown {
					run = false
				}
			default:
				// 忽略其他事件
			}
		}
	}
}

运行后截图:

最后:这里只讲 kafka的基本适合用,至于更深层的原理和部署细节以及技巧之后在写,留个位~

相关推荐
lingggggaaaa1 小时前
小迪安全v2023学习笔记(一百四十五讲)—— Webshell篇&魔改冰蝎&打乱特征指纹&新增加密协议&过后门查杀&过流量识别
笔记·学习·安全·魔改冰蝎·免杀对抗·免杀技术
Digitally1 小时前
如何将iPhone上的笔记传输到电脑
笔记·电脑·iphone
lkbhua莱克瓦243 小时前
Java基础——常用算法4
java·数据结构·笔记·算法·github·排序算法·快速排序
学渣676563 小时前
11111
笔记
MeowKnight9583 小时前
【DIY】PCB练习记录2——51单片机核心板
笔记
tjsoft10 小时前
网站如何被百度收录之探索笔记
笔记
QT 小鲜肉12 小时前
【个人成长笔记】在 Linux 系统下撰写老化测试脚本以实现自动压测效果(亲测有效)
linux·开发语言·笔记·单片机·压力测试
MeowKnight95812 小时前
【Qt】Qt实践记录2——TCP通信服务器和客户端demo
笔记·qt
编啊编程啊程12 小时前
【029】智能停车计费系统
java·数据库·spring boot·spring·spring cloud·kafka
钢门狂鸭13 小时前
go开发规范指引
开发语言·驱动开发·golang