前言
最近在思考 公司的golang消费脚本重启的时候,如果刚好消费到一半就重启了,那不就会数据丢失吗。kafka大家都知道后面的offset一旦提交,前面的都会被提交commit。那能不能在收到系统信号的时候,等待任务完成再退出当前程序呢?直接上代码
代码
package main
import (
"context"
"errors"
"fmt"
"github.com/segmentio/kafka-go"
"log"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
//1、kafka消费者正在消费中 等待消费完成再退出
//2、kafka 读取数据阻塞中 收到系统退出 不再阻塞 关闭reader
signals := make(chan os.Signal, 1)
stop := make(chan struct{},1)
go func() {
<-signals
close(stop)
}()
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
done := make(chan bool, 1)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
r := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{"127.0.0.1:9092"},
GroupID: "consumer-group",
Topic: "topic_test",
MinBytes: 1, // 1byte
MaxBytes: 10e6, // 10MB
StartOffset: kafka.LastOffset,
})
go func() {
defer func() {
if err := r.Close(); err != nil {
log.Fatal("failed to close reader:", err)
}
fmt.Println("close reader,exit.")
close(done)
}()
go func() {
<-stop
cancel()//cancel 是为了解决读消息阻塞 读不到系统退出消息
}()
for {
select {
case <-stop:
//任务处理完成后 检测到退出信号 退出
return
default:
// 从 Kafka 获取消息并进行处理
m, err := r.ReadMessage(ctx)
if errors.Is(err, context.Canceled) {
fmt.Println("cancel kafka block!")
return
} else if err != nil {
//handle err
continue
} else {
fmt.Println("task processing...")
fmt.Println(string(m.Value))
time.Sleep(time.Second * 10)
fmt.Println("task processing completed!")
}
}
}
}()
<-done
}
结果
- kafka阻塞取数据中,得到系统信号后,取消阻塞,退出
2.任务处理中,等待任务处理完成再退出