Golang消费脚本优雅重启

639 阅读1分钟

前言


最近在思考 公司的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

}

结果


  1. kafka阻塞取数据中,得到系统信号后,取消阻塞,退出

image.png 2.任务处理中,等待任务处理完成再退出

image.png