udp对实时性要求高的场景是一个避不开的话题,随着越来越多游戏使用go开发,一个好的go网络库必不可少。

做为网络库来说首先要做的是对底层连接的抽象,我们就用消息队列来抽象一个底层连接,在udp连接抽象为消息对内的过程中,会把一个地址和一个消息队列绑定。方便后续发送消息及对连接进行管理。通常我们会使用map来管理所有的消息队列,当然,sync.Map也是可以的。

代码片段如下:


一般使用udp的场景都是收发报非常快速的场景,虽然go给了我们一些非常方便的函数,可以很快构建一个udp服务器,但如果提高性能还是我们应该考虑的事情。

go接收udp消息的接口是ReadFromUDP,该接口会返回接受到的udp数据和来源地址。

但有个不可避免的问题,就是查找消息队列或者新建消息队列时会阻塞继续接收消息,会对吞吐量造成一定影响。


对此,我的做法是建立N个goroutine进行消息接收,同时使用锁,保证同一时刻只有一个goroutine调用ReadFromUDP,一旦调用完成,则释放锁,进行消息的处理,让下一个goroutine获得处理机会继续调用ReadFromUDP。


代码片段如下:

这样解决了ReadFromUDP的问他,但还有一个问题需要处理,因为我们只有一个map管理所有的消息队列,加锁是不可避免的(sync.Map也有加锁流程)所以我们需要减少这个map锁住的时间,考虑如下情况,两个goroutine都运行到处理消息的地方,但都是新连接,都需要新建消息队列,新建消息队列往往需要做一些处理,这样会不可避免加长锁的时间,所以这里我们使用两级锁来解决,一级是针对管理所有消息队列的map的,一级是针对消息队列本身的。


代码片段如下:

第一层锁只处理消息队列的管理,第二级锁则处理消息队列的新建。