概述
Go channel 是支持面向消息编程的基础,作为内置的语法特性,深受消息爱好者的喜爱之外,容易被初学者滥用,而错误使用chan的后果重则程序崩溃,轻则导致问题难以排查,开源的gopkg尝试提供一种新的接口代替chan,从功能上来说确实增强了鲁棒性,但是性能和使用流畅度恐怕不及Gopher预期。
details
Channel
Channel 模拟出了一个原生chan的基本方法,这些方法杜绝了几种panic或者危险可能:
- 重复关闭chan panic
- 写入已关闭chan panic
- 生产大于消费,同步消费则导致生产端阻塞,request超时进入恶性循环,异步消费则会有OOM风险,整个程序异常
- 无人消费,或者channel未被及时消费,会导致生产端直接阻塞卡死,而Go在语言层面难以及时报告这种错误
type Channel interface {
Input(v interface{})
Output() <-chan interface{}
Len() int
Stats() (produced uint64, consumed uint64)
Close()
}
channel struct
type channel struct {
size int
state int32
consumer chan interface{}
nonblock bool // non blocking mode
timeout time.Duration
timeoutCallback func(interface{})
producerThrottle Throttle
consumerThrottle Throttle
throttleWindow time.Duration
// statistics
produced uint64 // item already been insert into buffer
consumed uint64 // item already been sent into Output chan
// buffer
buffer *list.List // TODO: use high perf queue to reduce GC here
bufferCond *sync.Cond
bufferLock sync.Mutex
}
Input
- 封装v,增加timeout,尝试写入buffer(enqueueBuffer)
- 如果是阻塞模式则会cond.Wait在buffer有空闲
Output
直接返回一个原生chan,由于input时不是直接写入chan,因此这个output chan是具有流量控制能力的,lag都在buffer内解决
func (c *channel) Output() <-chan interface{} {
return c.consumer
}
Len
由2个原子变量计算出真实Len
func (c *channel) Len() int {
produced, consumed := c.Stats()
l := produced - consumed
return int(l)
}
Close
func (c *channel) Close() {
if !atomic.CompareAndSwapInt32(&c.state, 0, -1) {
return
}
// stop consumer
c.bufferLock.Lock()
c.buffer.Init() // clear buffer
c.bufferLock.Unlock()
c.bufferCond.Broadcast()
}