github.com/erda-project/erda-infra@v1.0.9/providers/kafka/batch_reader.go (about) 1 // Copyright (c) 2021 Terminus, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kafka 16 17 import ( 18 "time" 19 20 "github.com/confluentinc/confluent-kafka-go/kafka" 21 ) 22 23 // BatchReader . 24 type BatchReader interface { 25 ReadN(buf []interface{}, timeout time.Duration) (int, error) 26 Confirm() error 27 Close() error 28 } 29 30 // BatchReaderConfig . 31 type BatchReaderConfig struct { 32 Topics []string `file:"topics" desc:"topics"` 33 Group string `file:"group" desc:"consumer group id"` 34 Options map[string]interface{} `file:"options" desc:"options"` 35 } 36 37 // BatchReaderOption . 38 type BatchReaderOption interface{} 39 40 // WithReaderDecoder . 41 func WithReaderDecoder(dec Decoder) BatchReaderOption { 42 return BatchReaderOption(dec) 43 } 44 45 // Decoder . 46 type Decoder func(key, value []byte, topic *string, timestamp time.Time) (interface{}, error) 47 48 func (s *service) NewBatchReader(cfg *BatchReaderConfig, options ...BatchReaderOption) (BatchReader, error) { 49 var dec Decoder 50 for _, opt := range options { 51 switch v := opt.(type) { 52 case Decoder: 53 dec = v 54 } 55 } 56 if dec == nil { 57 dec = func(key, value []byte, topic *string, timestamp time.Time) (interface{}, error) { 58 return value, nil 59 } 60 } 61 kc := convertToConfigMap(mergeMap(s.p.Cfg.Comsumer.Options, cfg.Options)) 62 return newKafkaReader(s.p.Cfg.Servers, cfg.Group, cfg.Topics, kc, dec) 63 } 64 65 func newKafkaReader(servers, group string, topics []string, kc kafka.ConfigMap, dec Decoder) (BatchReader, error) { 66 kc["bootstrap.servers"] = servers 67 kc["group.id"] = group 68 kc["enable.auto.offset.store"] = false 69 kc["enable.auto.commit"] = false 70 delete(kc, "auto.offset.reset") 71 delete(kc, "auto.commit.interval.ms") 72 return &kafkaBatchReader{ 73 kc: kc, 74 topics: topics, 75 decode: dec, 76 }, nil 77 } 78 79 type kafkaBatchReader struct { 80 kc kafka.ConfigMap 81 topics []string 82 consumer *kafka.Consumer 83 decode Decoder 84 } 85 86 func (r *kafkaBatchReader) ReadN(buf []interface{}, timeout time.Duration) (int, error) { 87 if r.consumer == nil { 88 consumer, err := kafka.NewConsumer(&r.kc) 89 if err != nil { 90 return 0, err 91 } 92 err = consumer.SubscribeTopics(r.topics, nil) 93 if err != nil { 94 consumer.Close() 95 return 0, err 96 } 97 r.consumer = consumer 98 } 99 size := len(buf) 100 var offset int 101 maxWaitTimer := time.NewTimer(timeout * 3) 102 defer maxWaitTimer.Stop() 103 loop: 104 for { 105 if offset >= size { 106 break 107 } 108 select { 109 case <-maxWaitTimer.C: 110 break loop 111 default: 112 } 113 msg, err := r.consumer.ReadMessage(timeout) 114 if err != nil { 115 if kerr, ok := err.(kafka.Error); ok { 116 if kerr.Code() == kafka.ErrTimedOut { 117 return offset, nil 118 } 119 } 120 r.Close() 121 return offset, err 122 } 123 124 data, err := r.decode(msg.Key, msg.Value, msg.TopicPartition.Topic, msg.Timestamp) 125 if err != nil { 126 // ingore decode error 127 continue 128 } 129 130 _, err = r.consumer.StoreOffsets([]kafka.TopicPartition{msg.TopicPartition}) 131 if err != nil { 132 return offset, err 133 } 134 buf[offset] = data 135 offset++ 136 } 137 return offset, nil 138 } 139 140 func (r *kafkaBatchReader) Confirm() error { 141 if r.consumer != nil { 142 _, err := r.consumer.Commit() 143 if kerr, ok := err.(kafka.Error); ok { 144 if kerr.Code() == kafka.ErrNoOffset { 145 return nil 146 } 147 } 148 return err 149 } 150 151 return nil 152 } 153 154 func (r *kafkaBatchReader) Close() error { 155 consumer := r.consumer 156 if consumer != nil { 157 consumer.Unsubscribe() 158 err := consumer.Close() 159 r.consumer = nil 160 return err 161 } 162 return nil 163 } 164 165 // CommittedOffsets . 166 func (r *kafkaBatchReader) CommittedOffsets() ([]kafka.TopicPartition, error) { 167 consumer := r.consumer 168 if consumer == nil { 169 return nil, nil 170 } 171 ps, err := consumer.Assignment() 172 if err != nil { 173 return nil, err 174 } 175 return consumer.Committed(ps, 30*1000) 176 } 177 178 // CommittedOffsets . 179 func CommittedOffsets(r BatchReader) ([]kafka.TopicPartition, error) { 180 bw, ok := r.(*kafkaBatchReader) 181 if ok { 182 return bw.CommittedOffsets() 183 } 184 return nil, nil 185 }