gitee.com/woood2/luca@v1.0.4/cmd/consumer/internal/subscriber/retry_consumer.go (about) 1 package subscriber 2 3 import ( 4 "context" 5 "gitee.com/woood2/luca/cmd/consumer/internal/alarm" 6 "github.com/Shopify/sarama" 7 "go.uber.org/zap" 8 "runtime/debug" 9 "time" 10 ) 11 12 // RetryConsumer represents a Sarama consumer group consumer 13 type RetryConsumer struct { 14 GroupID string 15 MsgHandler MsgHandler 16 Logger *zap.Logger 17 } 18 19 // Setup is run at the beginning of a new session, before ConsumeClaim 20 func (consumer *RetryConsumer) Setup(sarama.ConsumerGroupSession) error { 21 return nil 22 } 23 24 // Cleanup is run at the end of a session, once all ConsumeClaim goroutines have exited 25 func (consumer *RetryConsumer) Cleanup(sarama.ConsumerGroupSession) error { 26 return nil 27 } 28 29 // ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages(). 30 func (consumer *RetryConsumer) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { 31 for message := range claim.Messages() { 32 retry := 1 33 delay := 2 * time.Second 34 maxDelay := 5 * time.Minute 35 ch := make(chan bool) 36 retryLoop: 37 for { 38 go func() { 39 rst := consumer.handle(message, retry) //长任务注意幂等性 40 select { 41 case ch <- rst: 42 return 43 case <-session.Context().Done(): 44 select { 45 case ch <- rst: 46 default: //防止goroutine泄漏 47 } 48 return 49 } 50 }() 51 select { 52 case <-session.Context().Done(): 53 select { 54 case <-time.NewTimer(5 * time.Second).C: 55 return nil 56 case b := <-ch: 57 if b { 58 session.MarkMessage(message, "") 59 } else { 60 alarm.Yell(retry, consumer.GroupID, message) 61 } 62 return nil 63 } 64 case b := <-ch: 65 if b { 66 session.MarkMessage(message, "") 67 select { 68 case <-session.Context().Done(): 69 return nil 70 default: 71 break retryLoop 72 } 73 } else { 74 alarm.Yell(retry, consumer.GroupID, message) 75 select { 76 case <-session.Context().Done(): 77 return nil 78 case <-time.NewTimer(delay).C: 79 } 80 retry++ 81 delay = min(delay*2, maxDelay) 82 } 83 } 84 } 85 } 86 return nil 87 } 88 89 func (consumer *RetryConsumer) handle(msg *sarama.ConsumerMessage, retry int) bool { 90 defer func() { 91 if r := recover(); r != nil { 92 consumer.Logger.Error("panic recover", 93 zap.Any("err", r), 94 zap.String("topic", msg.Topic), 95 zap.String("groupID", consumer.GroupID), 96 zap.Int32("partition", msg.Partition), 97 zap.Int64("offset", msg.Offset), 98 zap.String("stack", string(debug.Stack())), 99 ) 100 } 101 }() 102 ctx, cancel := context.WithCancel(context.Background()) 103 defer cancel() 104 rst := consumer.MsgHandler(ctx, msg, retry) 105 return rst 106 } 107 108 func min(a, b time.Duration) time.Duration { 109 if a >= b { 110 return b 111 } else { 112 return a 113 } 114 }