Skip to content

Assign a workder to each link, avoid interactions when multiple links are processed by the same worker (为每个链接分配一个workder,避免同一worker处理多个链接时的互相影响)#231

Merged
aceld merged 17 commits intoaceld:masterfrom
LI-GUOJIE:master
Jun 14, 2023

Conversation

@LI-GUOJIE
Copy link
Collaborator

No description provided.

LI-GUOJIE and others added 8 commits May 11, 2023 11:21
…ed and packaged during massive online player interaction from N^2 to N

(优化广播消息,将大规模在线玩家互动时消息序列化与打包的次数从N^2降到N)
… are processed by the same worker

(为每个链接分配一个workder,避免同一worker处理多个链接时的互相影响)
… are processed by the same worker

(为每个链接分配一个workder,避免同一worker处理多个链接时的互相影响)
@LI-GUOJIE
Copy link
Collaborator Author

Assign a workder to each link, avoid interactions when multiple links are processed by the same worker
(为每个链接分配一个workder,避免同一worker处理多个链接时的互相影响)

@LI-GUOJIE LI-GUOJIE changed the title Optimize broadcast functions(优化消息广播) Assign a workder to each link, avoid interactions when multiple links are processed by the same worker (为每个链接分配一个workder,避免同一worker处理多个链接时的互相影响) May 13, 2023
LI-GUOJIE added 2 commits May 13, 2023 18:10
… are processed by the same worker

(为每个链接分配一个workder,避免同一worker处理多个链接时的互相影响)
@LI-GUOJIE
Copy link
Collaborator Author

The implementation idea is to set the workerPoolSize to MaxConn when starting up, and create a FreeWorkerIDMap that contains all worker IDs.
Every time a connection is created, a worker ID is obtained from the FreeWorkerIDMap.
Every time a connection is destroyed, the worker ID is returned to the FreeWorkerIDMap.
You can enable this new strategy by modifying the value of WorkerMode in zinx.json, otherwise the code will not change.

(实现的思路是,启动时将workerPoolSize设为MaxConn, 并创建一个包含所有workerID的FreeWorkerIDMap。
每次创建一个连接,从FreeWorkerIDMap里获取一个workerid。
每次销毁一个链接,将workerid放回FreeWorkerIDMap。
你可以通过在zinx.json中修改WorkerMode的值来启用这个新的策略,否则代码不会有任何变化。)

@LI-GUOJIE LI-GUOJIE closed this Jun 12, 2023
@LI-GUOJIE LI-GUOJIE force-pushed the master branch 2 times, most recently from 0be5939 to 37b1fe5 Compare June 12, 2023 10:20
@LI-GUOJIE LI-GUOJIE reopened this Jun 12, 2023
@aceld
Copy link
Owner

aceld commented Jun 13, 2023

@LI-GUOJIE nice, 今天详细看了PR,感觉思路很棒。

@aceld
Copy link
Owner

aceld commented Jun 13, 2023

@LI-GUOJIE 我有几个疑问,希望给一些解答哈:

1,从workerID 分配制的, 和之前轮询的会有什么本质的区别,比如适合解决哪些场景的调度,其优势突出的地方希望能提示下。
2,这种分配会不会导致,worker工作池的链接分配不平均,负载不均衡问题

感谢啦

@LI-GUOJIE
Copy link
Collaborator Author

@LI-GUOJIE nice, 今天详细看了PR,感觉思路很棒。

感谢作者的肯定 :D

@LI-GUOJIE
Copy link
Collaborator Author

@LI-GUOJIE 我有几个疑问,希望给一些解答哈:

1,从workerID 分配制的, 和之前轮询的会有什么本质的区别,比如适合解决哪些场景的调度,其优势突出的地方希望能提示下。 2,这种分配会不会导致,worker工作池的链接分配不平均,负载不均衡问题

感谢啦

之前的轮询方式
每个worker上的链接数是不确定的,不能保证完全平均。
当业务中存在RPC调用时,CPU将进入短暂的sleep,此时这名worker所负责的其他task只能排队等待,最终会造成CPU占用不高但并发量上不去的现象。
而在轮询的基础上,若是直接为每个task启动新的goroutine,则会导致消息乱序,且goroutine规模不可控。

新版分配机制
worker和链接的关系永远是1比1,因此不会存在不均衡的问题。
当一个链接上发生RPC调用时,其他链接上的task不会受到任何影响,CPU得以充分利用,且消息依然是有序的。

适用场景:
当项目中存在大量RPC调用或数据库访问时,新版分配机制在性能和开发效率上都非常高。
新版分配机制会创建出和链接数一样多的goroutine,假设链接数10000,会消耗20MB内存。

@aceld
Copy link
Owner

aceld commented Jun 13, 2023

了解了,我看修改的部分是兼容之前的逻辑的,只不过需要在配置中增加 WorkerMode即可。

还有几点小建议,辛苦 @LI-GUOJIE 看看是否可以修正,重新提交下。

1)WorkerMode的取值常字符串OneWorkerEachConn 单词有点过于复杂,能否变得简单一些,方便开发在配置文件进行配置。我想了下,如果不配置,或者配置"",则表示之前的,之前的应该是 取余形式,所以应该算是一种Hash Mode,你这个测试1对1的绑定,类似是一种Bind Mode,当然你可以想出来更好的简单的描述,来确定这个特性。

2)另外在内部进行对OneWorkerEachConn的判断,是否可以通用一个宏来代替,这样今后方便替换常字符串。

3)该功能为非对外接口功能,类似UseWorker(),FreeWorker()等是否可以改成私有函数,防止其他模块直接调用,导致不可预知的风险。

辛苦再简单修正下,重新commit下哈。

再次感谢提交PR!辛苦!

另,如果MaxPoolSize满了,链接还是否可以绑定到worker,如果绑定不上会发生什么?

@LI-GUOJIE
Copy link
Collaborator Author

LI-GUOJIE commented Jun 13, 2023

1)OneWorkerEachConn的确太冗长了,就改成Bind吧。如果WorkerMode不存在,或配置为""或“Hash",都用之前的取余分配。
2)抱歉,这里写的太随意了,我参照之前的代码风格添加一下。
3)好的,的确应该让它私有,我思考一下怎么修改哈。
4)MaxPoolSize如果满了,则UseWorker总是返回0,会导致负载不均衡。
Bind模式下,MaxPoolSize等于MaxConn,按理说满了是不会再调用UseWorker的。
Accept中判断了如果connection已满,则不会接受新的连接,直到已创建的连接数减少。
以防万一,在这种异常情况下,我就用之前的取余处理吧?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

其他都review哈,没问题 👍🏻! 但是这个decode的转换还不确定。

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里删除了类型转换,是因为变量本身就是int64类型:
frameLength := d.getUnadjustedFrameLength

然后,.golangci.yaml中有一行:
unconvert // 禁止冗余的类型转换

这导致PR无法通过GIthub的自动检测。

作者辛苦了😄

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LI-GUOJIE 妥,已merge

@aceld aceld merged commit cd8e4a1 into aceld:master Jun 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants