@
asuraa #24
> 1.2048 这个值是有考究的
硬件配置差别很大, 1c1g/16c64g/36c/512g, 对于这点协程数量的压力完全不同, 所以不论什么吋间规格都用 2048 写死了, 真没什么解释的必要.
> 2.worker 分配不均匀问题
可以参考下我的 nbio 里的实现:
https://github.com/lesismal/nbio/blob/master/conn.go#L185原理是, 每个 conn 上一个执行队列, 每个 job 入队列并判断是不是队首, 如果是队首, 就用通用的协程池去异步循环处理挨个取出直到没有任务; 如果不是队首, 说明已有协程在循环执行, 则入队后直接返回并等待被执行即可. 这种实现下, 协程池的部分是不受限制的, 如果对协程池 size 没有限制就直接 go func(), 如果对协程池 size 有限制, 则协程池实现的部分限制即可.
游戏服务, 通常对最大在线量本身就有限制, 在 Acceptor 或者 login 的部分就可以做限制, 而且通常单节点在线量不会超大, 例如单节点 5k-5w 在线量, 通常都是 1w 以内, 而这种在线量在 8c16g 的机器上即使对应的每个连接一个异步任务的协程也压力不大, 而且 go func()执行完当前任务队列没任务后即可退出所以不需要持续占用协程, 再考虑实际并行和下游基础设施连接池之类的, 临时异步任务并发度对应的协程数量也多数小于实际连接数.
协程池的实现大概几种, 早期 ants 那种自己用 cond_t+队列或者 chan 配合 idle time idle size 的常驻协程方案并没有性能优势而且常驻协程的释放不及时, 而且 cond_t+毒烈或者 chan 的性能都有些损失, 除了可以限制协程数量, 绝大多数场景不如 go func().
字节家的 workerpool 是 size 限制 + list + go func() , 新任务判断当前协程池 size 并发度, 如果没达到限制就直接 go func()去循环执行 list 队列里的 job, 如果达到 size 限制里就入 list 队列等待被执行, 每个 go func()循环执行直到 list 队列里没有 job 里就退出. 这种方案是通用协程池, size 设置为 1 则也可以直接作为我上面说的 nbio 里每个 conn 的执行队列的方案, nbio 每个 conn 的执行队列没有用 list 而是用 slice, 是因为考虑单个 conn 任务数量不会太大所以 slice len 也不会太大, 所以用 slice 做队列复用并且省去了 pool get/put 之类的额外逻辑, 相比于 list 可能略有优势.
较高版本的 go, runtime 协程复用很好了, 如果不考虑 size 限制, 其他协程池实现基本都不如直接 go func()性能好, 字节家的这种 go func()好处是只比 go func()多了一点点逻辑但非常接近 go func()了, 但也有个缺点, 就是对 list 队列 size 没有限制, 如果下游阻塞并且上游不断有新的 job 进来, 理论上是存在无限增长的风险的.
lxzan
https://github.com/lxzan/concurrency 与字节的类似, 用于队列的数据结构略有不同.
nbio 的协程池为了更均衡, 达到 size 限制前直接 go func(), 达到 size 了, 用 chan 做队列, chan 的好处是满了就阻塞了可以让上下游自动均衡, 规避了字节家 workerpool 的缺陷:
https://github.com/lesismal/nbio/blob/master/taskpool/taskpool.gogolang runtime, 例如 8c16g 这种配置, 通常的场景下, 10w 个协程数量都算还好, 压力不大. 而且 nbio 的协程池 size 默认是按核心数初始化, 多数也是跑不满 size 的. 而且用户也可以自己随便配置用什么协程池, 因为不管用什么协程池, nbio 的每个 conn 已经保证了自己的有序所以不需要协程池去保证顺序
> 3.代码估计没看仔细,是只有获取没创建的才需要初始化加锁的,后续获取是不需要的
这个确实没仔细看, 只是简单扫了几眼, 你说的是对的
> 这模块我们自己项目也在用,我们一百多万 dau ,跑的好好的。不过代码是比较简单,大家相互讨论印证也没问题。挺好的技术交流
因为 golang 本身性能还不错, 即使框架代码一般, 对于绝大多数的非性能和开销敏感的业务而言也是过剩的.
但这并不意味着这种框架本身优秀.
而且如 OP 在#4 和我在#11 说的, 简单游戏业务, 游戏脚手架就那几个简单的组建, 网络库的部分算是大头的了, 其他的太多现成的甚至标准库直接就够用了, 真的挺简单的 😂😂😂