适用版本
- go micro v4或以上
项目地址
背景
最近在用go micro做事件驱动架构的微服务,使用的broker插件为“github.com/go-micro/plugins/v4/broker/kafka” ,插件默认使用同步生产者,但在kafka节点多的时候同步生产者效率较低,尤其是设置WaitForALL的时候延迟升高,2个节点就达到107ms所以只能改成异步生产者,阅读官方文档得知可以通过AsyncProducer方法设置,代码如下:
func AsyncProduceMessage() {
var errorsChan = make(chan *sarama.ProducerError)
var successesChan = make(chan *sarama.ProducerMessage)
go func() {
for err := range errorsChan {
fmt.Println(err)
}
}
go func() {
for v := range successesChan {
fmt.Println(v)
}
}
b := NewBroker(AsyncProducer(errorsChan,successesChan))
b.Publish(`topic`, &broker.Message{})
}
我按照官方文档的提示在自己的包下创建broker,代码如下(可以通过项目地址查看,示例代码忽略其他片段):
successChan := make(chan *sarama.ProducerMessage, conf.Broker.Kafka.ChannelBufferSize)
errorChan := make(chan *sarama.ProducerError, conf.Broker.Kafka.ChannelBufferSize)
var eb event.Listener
broker := infrastructure.NewKafkaBroker(conf.Broker.Kafka, kafkabroker.AsyncProducer(errorChan, successChan))
successChan和errorChan还设置到自编的事件侦听器里,打开2个Goroutine监听,回调处理包含日志、链路追踪、死信主题等多项操作,结果消息推送成功后没有执行,说明根本没有用到2个创建好的channel很有可能是源码的问题。
原因分析
我们先定位到kafka插件里设置异步生产者管道的源码:
func AsyncProducer(errors chan<- *sarama.ProducerError, successes chan<- *sarama.ProducerMessage) broker.Option {
// set default opt
var opt = func(options *broker.Options) {}
if successes != nil {
opt = setBrokerOption(asyncProduceSuccessKey{}, successes)
}
if errors != nil {
opt = setBrokerOption(asyncProduceErrorKey{}, errors)
}
return opt
}
从源码可以看出opt的设置失效没有把两个channel设置进去,只要把它们放入函数体即可。修改后的效果如下:
func AsyncProducer(errors chan<- *sarama.ProducerError, successes chan<- *sarama.ProducerMessage) broker.Option {
// set default opt
return func(options *broker.Options) { // 把2个channel配置到options
if errors != nil {
setBrokerOption(asyncProduceErrorKey{}, errors)(options)
}
if successes != nil {
setBrokerOption(asyncProduceSuccessKey{}, successes)(options)
}
}
}
这样就能引用外部传入的channel让options生效,我们可以放心地使用异步生产者,此时可以发现请求的延迟明显降低,因为异步生产者的原理是推入channel以后即返回成功,由sarama底层去发送消息,回调处理也能正常进行。
这是一个源码级的Bug,Github上有人提交了相关的PR但不知道怎么回事一直没有merge,PR地址:github.com/go-micro/pl… 。