Golang那些坑-rand.Rand高并发panic

502 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

背景:

kafka性能优化时发现,资源扩充后,出现消费QPS不平稳问题,消费延迟20s,kafka频繁rebalance现象

Rebalance

什么是rebalance?是当消费者组发生变化,或者partition发生变化时。根据上面提到的某种策略进行对消费者进行分区的分配。

发生 rebalance 的时机:

  1. 组成员个数发生变化。例如有新的 consumer 实例加入该消费组或者离开组,获消费者挂了
  2. 订阅 topic 的分区数发生变化(订阅新的主题也可以理解成分区发生变化)

Rebalance 发生时,Group 下所有 Consumer 实例都会协调在一起共同参与,Kafka 能够保证尽量达到最公平的分配。但是 Rebalance 过程对 Consumer Group 会造成比较严重的影响。在 Rebalance 的过程中 Consumer Group 下的所有消费者实例都会停止工作(Stop The World),等待 Rebalance 过程完成。

原因:

var (
   randomEngine = rand.New(rand.NewSource(time.Now().UnixNano()))
)
并发调用:randomEngine.Intn(10)

rand.Rand 并发panic: index out of range, 并发低时不会出现,并发高场景才能触发

解决:

rand线程不安全,safeRander解决

并发调用:randomEngine.Intn(10)
var (
   randomEngine = NewSafeRander()
)
// SafeRander is used for avoiding to use global's rand;
type SafeRander struct {
   pos     uint32
   randers [128]*rand.Rand
   locks   [128]*sync.Mutex
}

// NewSafeRander .
func NewSafeRander() *SafeRander {
   var randers [128]*rand.Rand
   var locks [128]*sync.Mutex
   for i := 0; i < 128; i++ {
      randers[i] = rand.New(rand.NewSource(time.Now().UnixNano()))
      locks[i] = new(sync.Mutex)
   }
   return &SafeRander{
      randers: randers,
      locks:   locks,
   }
}

// Intn .
func (sr *SafeRander) Intn(n int) int {
   x := atomic.AddUint32(&sr.pos, 1)
   x %= 128
   sr.locks[x].Lock()
   n = sr.randers[x].Intn(n)
   sr.locks[x].Unlock()
   return n
}