go: 如何编写一个正确的udp服务端

2022-11-29 17:24:31 浏览数 (1)

udp的服务端有一个大坑,即如果收包不及时,在系统缓冲写满后,将大量丢包。 在网上通常的示例中,一般在for循环中执行操作逻辑。这在生产环境将是一个隐患。 go强大简易的并发能力可以用在处理udp数据上。

代码语言:javascript复制
 PoolSizeUDP :=  
    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并发必然踩内存。
udp

0 人点赞