github.com/erda-project/erda-infra@v1.0.9/providers/kafka/producer.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 "encoding/json" 19 "sync" 20 "time" 21 22 "github.com/confluentinc/confluent-kafka-go/kafka" 23 "github.com/recallsong/go-utils/reflectx" 24 25 "github.com/erda-project/erda-infra/base/logs" 26 writer "github.com/erda-project/erda-infra/pkg/parallel-writer" 27 ) 28 29 // Message . 30 type Message struct { 31 Topic *string 32 Data []byte 33 Key []byte 34 } 35 36 // StringMessage . 37 type StringMessage struct { 38 Topic *string 39 Data string 40 } 41 42 // ProducerConfig . 43 type ProducerConfig struct { 44 Topic string `file:"topic" env:"KAFKA_P_TOPIC" desc:"topic"` 45 Parallelism uint64 `file:"parallelism" env:"KAFKA_P_PARALLELISM" default:"4" desc:"parallelism"` 46 Batch struct { 47 Size uint64 `file:"size" env:"KAFKA_P_BATCH_SIZE" default:"100" desc:"batch size"` 48 Timeout time.Duration `file:"timeout" env:"KAFKA_P_BUFFER_TIMEOUT" default:"30s" desc:"timeout to flush buffer for batch write"` 49 } `file:"batch"` 50 Shared bool `file:"shared" default:"true" desc:"shared producer instance"` 51 Options map[string]interface{} `file:"options" desc:"options"` 52 } 53 54 // ProducerOption . 55 type ProducerOption interface { 56 errHandler() func(error) error 57 } 58 59 type producerOption struct{ _eh func(error) error } 60 61 func (p *producerOption) errHandler() func(error) error { return p._eh } 62 63 // WithAsyncWriteErrorHandler . 64 func WithAsyncWriteErrorHandler(eh func(error) error) ProducerOption { 65 return &producerOption{_eh: eh} 66 } 67 68 func newProducer(servers string, extra map[string]interface{}, log logs.Logger) (*kafka.Producer, error) { 69 kc := kafka.ConfigMap{"go.batch.producer": true} 70 if extra != nil { 71 for k, v := range extra { 72 kc[k] = v 73 } 74 } 75 kc["bootstrap.servers"] = servers 76 kp, err := kafka.NewProducer(&kc) 77 if err != nil { 78 return nil, err 79 } 80 go consumeEvents(kp, log) 81 return kp, err 82 } 83 84 func consumeEvents(kp *kafka.Producer, log logs.Logger) { 85 for e := range kp.Events() { 86 switch ev := e.(type) { 87 case *kafka.Message: 88 if ev.TopicPartition.Error != nil { 89 log.Errorf("Kafka delivery failed: %v", ev.TopicPartition) 90 } 91 } 92 } 93 log.Debugf("exit kafka events consumer") 94 } 95 96 type sharedProducer struct { 97 lock sync.Mutex 98 instance *kafka.Producer 99 refs int 100 log logs.Logger 101 } 102 103 func (p *sharedProducer) release() error { 104 p.lock.Lock() 105 defer p.lock.Unlock() 106 if p.refs == 0 { 107 return nil 108 } 109 p.refs-- 110 if p.refs == 0 { 111 p.instance.Close() 112 } 113 return nil 114 } 115 116 func (p *sharedProducer) get(servers string, extra map[string]interface{}) (*kafka.Producer, error) { 117 p.lock.Lock() 118 defer p.lock.Unlock() 119 if p.refs == 0 { 120 kp, err := newProducer(servers, extra, p.log) 121 if err != nil { 122 return nil, err 123 } 124 p.instance = kp 125 } 126 p.refs++ 127 return p.instance, nil 128 } 129 130 type producer struct { 131 kp *kafka.Producer 132 close func() error 133 topic string 134 } 135 136 func (p *producer) ProduceChannelSize() int { 137 return len(p.kp.ProduceChannel()) 138 } 139 140 func (p *producer) EventsChannelSize() int { 141 return len(p.kp.Events()) 142 } 143 144 func (p *producer) Write(data interface{}) error { 145 return p.publish(data) 146 } 147 148 func (p *producer) WriteN(data ...interface{}) (int, error) { 149 for i, item := range data { 150 err := p.publish(item) 151 if err != nil { 152 return i, err 153 } 154 } 155 return len(data), nil 156 } 157 158 func (p *producer) publish(data interface{}) error { 159 var ( 160 bytes []byte 161 key []byte 162 ) 163 topic := &p.topic 164 switch val := data.(type) { 165 case *Message: 166 if val.Topic != nil { 167 topic = val.Topic 168 } 169 bytes = val.Data 170 key = val.Key 171 case *StringMessage: 172 if val.Topic != nil { 173 topic = val.Topic 174 } 175 bytes = reflectx.StringToBytes(val.Data) 176 case []byte: 177 bytes = val 178 case string: 179 bytes = reflectx.StringToBytes(val) 180 default: 181 data, err := json.Marshal(data) 182 if err != nil { 183 return err 184 } 185 bytes = data 186 } 187 p.kp.ProduceChannel() <- &kafka.Message{ 188 Value: bytes, 189 Key: key, 190 TopicPartition: kafka.TopicPartition{Topic: topic, Partition: kafka.PartitionAny}, 191 } 192 return nil 193 } 194 195 func (p *producer) Close() error { 196 return p.close() 197 } 198 199 func (s *service) NewProducer(c *ProducerConfig, options ...ProducerOption) (writer.Writer, error) { 200 var eh writer.ErrorHandler = s.producerError 201 for _, item := range options { 202 if item != nil && item.errHandler() != nil { 203 eh = item.errHandler() 204 } 205 } 206 if c.Shared { 207 kp, err := s.p.producer.get(s.p.Cfg.Servers, s.p.Cfg.Producer.Options) 208 if err != nil { 209 return nil, err 210 } 211 return writer.ParallelBatch(func(uint64) writer.Writer { 212 return &producer{ 213 kp: kp, 214 close: s.p.producer.release, 215 topic: c.Topic, 216 } 217 }, c.Parallelism, c.Batch.Size, c.Batch.Timeout, eh), nil 218 } 219 kp, err := newProducer(s.p.Cfg.Servers, c.Options, s.log) 220 if err != nil { 221 return nil, err 222 } 223 return writer.ParallelBatch(func(uint64) writer.Writer { 224 return &producer{ 225 kp: kp, 226 close: func() error { 227 kp.Close() 228 return nil 229 }, 230 topic: c.Topic, 231 } 232 }, c.Parallelism, c.Batch.Size, c.Batch.Timeout, eh), nil 233 } 234 235 func (s *service) producerError(err error) error { 236 s.log.Errorf("fail to write kafka: %s", err) 237 return nil // skip error 238 } 239 240 func (s *service) ProduceChannelSize() int { 241 s.p.producer.lock.Lock() 242 defer s.p.producer.lock.Unlock() 243 if s.p.producer.instance == nil { 244 return 0 245 } 246 return len(s.p.producer.instance.ProduceChannel()) 247 } 248 249 func (s *service) ProduceEventsChannelSize() int { 250 s.p.producer.lock.Lock() 251 defer s.p.producer.lock.Unlock() 252 if s.p.producer.instance == nil { 253 return 0 254 } 255 return len(s.p.producer.instance.Events()) 256 }