关于消息队列的问题
既然每个cli的链接都会创建一个goroutine,干嘛非要再用消息队列来处理消息,直接在conn的goroutine里处理消息不就可以了。
加消息队列也是为了限定处理业务worker的个数,并不能无限制的开辟goroutine,考虑到对于大并发的情况。
那cli链接的goroutine能不能控制数量,有多少cli就只能创建多少个goroutine?
并不是,举个栗子。 比如现在有10W个客户端请求,那么我们之前如果不加消息队列和worker池机制,以目前的框架来讲是 会开30w个go(10w个reader,10w个writer,10w个处理消息业务的Handle),其中Reader,Writer是读写客户端数据的,而且大部分时间是阻塞状态,并不会抢占调度器资源。但是10w个handle确实处理业务的,需要cpu计算,那么调度器需要在这10w的go之间进行切换,那么切换成本就浪费了cpu的利用率。
那么如果用worker机制,比如我们限定worker的数量就是8个(建议cpu核心也是8个),那么还是这个例子,有10w的客户端请求过来,zinx会开辟(10w个reader[阻塞],10W个writer[阻塞],和8个worker用来调用Handle),那么最终处理业务的go只有8个,调度器只需要在这8个之间切换就可以了,所以不管客户端请求数量多达,最后也是8个go在做真正的计算,大大节省了调度器的切换成本。
啊哈,总算是理解了。谢谢您的详细解答。
@aceld 如果是为了限定用户逻辑handle并发,你用协程池worker来收敛并发是合理的。 但如果只是为了减轻goroutine增删, morestack及调度的消耗,那就不必了,pingcap以前有个pr已经说明是 不加协程池比加协程池更有效率。
@rfyiamcool 多谢,我去pingcap项目去看看,学习一下
@rfyiamcool 可以贴出这个pr吗 tidb里 没搜到相关的
并不是,举个栗子。 比如现在有10W个客户端请求,那么我们之前如果不加消息队列和worker池机制,以目前的框架来讲是 会开30w个go(10w个reader,10w个writer,10w个处理消息业务的Handle),其中Reader,Writer是读写客户端数据的,而且大部分时间是阻塞状态,并不会抢占调度器资源。但是10w个handle确实处理业务的,需要cpu计算,那么调度器需要在这10w的go之间进行切换,那么切换成本就浪费了cpu的利用率。
那么如果用worker机制,比如我们限定worker的数量就是8个(建议cpu核心也是8个),那么还是这个例子,有10w的客户端请求过来,zinx会开辟(10w个reader[阻塞],10W个writer[阻塞],和8个worker用来调用Handle),那么最终处理业务的go只有8个,调度器只需要在这8个之间切换就可以了,所以不管客户端请求数量多达,最后也是8个go在做真正的计算,大大节省了调度器的切换成本。
如果只有8个worker做业务逻辑,当需要有阻塞操作的时候(如读取redis数据),此时8个都阻塞在等待redis返回,这时候会不会造成所有请求都无法处理了
并不是,举个栗子。 比如现在有10W个客户端请求,那么我们之前如果不加消息队列和worker池机制,以目前的框架来讲是 会开30w个go(10w个reader,10w个writer,10w个处理消息业务的Handle),其中Reader,Writer是读写客户端数据的,而且大部分时间是阻塞状态,并不会抢占调度器资源。但是10w个handle确实处理业务的,需要cpu计算,那么调度器需要在这10w的go之间进行切换,那么切换成本就浪费了cpu的利用率。 那么如果用worker机制,比如我们限定worker的数量就是8个(建议cpu核心也是8个),那么还是这个例子,有10w的客户端请求过来,zinx会开辟(10w个reader[阻塞],10W个writer[阻塞],和8个worker用来调用Handle),那么最终处理业务的go只有8个,调度器只需要在这8个之间切换就可以了,所以不管客户端请求数量多达,最后也是8个go在做真正的计算,大大节省了调度器的切换成本。
如果只有8个worker做业务逻辑,当需要有阻塞操作的时候(如读取redis数据),此时8个都阻塞在等待redis返回,这时候会不会造成所有请求都无法处理了
如果是这样的话,比如有DB或者其他IO的阻塞在worker里,就会出现当流程等待的情况,所以就要在业务里添加设计,比如,你存储redis,应该有一个专门存储redis或io的 server或者gateway。然后worker以非阻塞的形式发送给这个存储的gateway。当存储结果回来,或者db/redis的查询结果回来,也是以一个msg的形式返回给zinx。如果是一定是业务需要同步处理,就是读取是一定遵循顺序的,业务1-->业务2,但是业务2一定要求具备业务1从db查询返回来的条件才能执行,那么可能没办法了,你的瓶颈就在DB这块。只能加快你DB的速度,或者优化redis缓存的设计了。 如果您有更好的解决方案,也可以聊一下,一起讨论学习。