ond)
go addQueue()
time.Sleep(1000 * time.Second)
}
func addQueue() {
for i := 0; i < 100; i++ {
// 新建一个任务
payLoad := Payload{Num: i}
work := Job{Payload: payLoad}
// 任务放入任务队列channal
JobQueue <- work
fmt.Println("JobQueue <- work", i)
fmt.Println("当前协程数:", runtime.NumGoroutine())
time.Sleep(100 * time.Millisecond)
}
}
执行结果如下:
...
...
JobQueue <- work 97
当前协程数: 100
job := <-JobQueue:
等待空闲worker (任务多的时候会阻塞这里
JobQueue <- work 98
当前协程数: 101
job := <-JobQueue:
等待空闲worker (任务多的时候会阻塞这里
JobQueue <- work 99
当前协程数: 102
我们发现,我们依然没能控制住协程数量,我们只是控制住了worker的数量。这种情况下,如果worker数量设置的合理且异步任务耗时不是特别长的情况下一般没有问题。但是出于安全的考虑,我要把这个阻塞的协程数做一个控制,如果达到限制时候拒绝服务以保护系统该怎么处理?
真正控制协程数量(并发执行的任务数)
我们可以控制并发执行(包括等待执行)的任务数。我们加入任务使用如下判断。用一个带缓冲的Channel控制并发执行的任务数。当任务异步处理完成的时候执行<- DispatchNumControl
释放控制即可。用这种方法,我们可以根据压测结果设置合适的并发数从而保证系统能够尽可能的发挥自己的能力,同时保证不会因为任务量太大而崩溃(因为达到极限的时候,系统会告诉调用方--我很忙)。
//用于控制并发处理的协程数
var DispatchNumControl = make(chan bool, 10000)
func Limit(work Job) bool {
select {
case <-time.After(time.Millisecond * 100):
fmt.println("我很忙")
return false
case DispatchNumControl <- true:
// 任务放入任务队列channal
jobChannel <- work
return true
}
}
总结
总结一波,协程是个好的设计,但任何东西都不能过度使用。我们做系统设计的时候,一定也要时刻想着控制--要对自己设计的系统有着足够的控制力。