go: 如何编写一个正确的udp服务端 udp的服务端有一个大坑,即如果收包不及时,在系统缓冲写满后,将大量丢包。 在网上通常的示例中,一般在`for`循环中执行操作逻辑。这在生产环境将是一个隐患。 go强大简易的并发能力可以用在处理udp数据上。 ``` PoolSizeUDP := 4096 listener, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.ParseIP(listenIP), Port: port, }) if err != nil { logrus.Fatalf("RunUdpServer failed to listen: %v", err) return nil } var data = make([]byte, PoolSizeUDP) chLimit := make(chan int, 64) // 最多创建64个协程,避免内存爆炸 for { select { case <-ctx.Done(): return nil default: } n, addr, err := listener.ReadFromUDP(data) if err != nil { logrus.Errorf("RunUdpServer ReadFromUDP err: %v", err) continue } raw := make([]byte, n) // 重点注意,每次循环都必须创建新的raw变量,否则踩内存 copy(raw, data[:n]) chLimit <- 1 go func(udpMsg []byte) { // 拿 udpMsg 做点什么 defer func() { <-chLimit }() DoSth(udpMsg) }(raw) } ``` 注意点: 1. data可以在循环外创建,复用即可。 2. 不要在`for`中执行重逻辑,避免等待太久时间udp大量丢包。所以每次收到udpMsg,都交给go协程来处理。 3. raw必须每次在循环内创建,否则在后面的`go`并发必然踩内存。 来自 大脸猪 写于 2022-06-24 15:41 -- 更新于2022-06-24 15:48 -- 0 条评论