Closed czy0538 closed 1 year ago
I found that this issue is related to the gnet.WithMulticore(multicore) option. When this option is set to true, this issue occurs, and the number of duplicated messages is equal to the number of CPU cores I have, but it is not affected by GOMAXPROCS.
I tested it using the simplest echo program, and here are the test results:
❯ GOMAXPROCS=1 go run ./echo.go --multicore=false
1
Hello world
^Csignal: interrupt
❯ GOMAXPROCS=1 go run ./echo.go --multicore=true
1
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
^Csignal: interrupt
❯ go run ./echo.go --multicore=true
8
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
^Csignal: interrupt
Here are my test code:
package main
import (
"flag"
"fmt"
"log"
"runtime"
"github.com/panjf2000/gnet/v2"
)
type echoServer struct {
gnet.BuiltinEventEngine
}
func (es *echoServer) OnTraffic(c gnet.Conn) gnet.Action {
data, _ := c.Next(-1)
newData := make([]byte, len(data))
copy(newData, data)
go func(data []byte) {
fmt.Println(string(data))
}(newData)
// c.Write(data)
return gnet.None
}
func main() {
fmt.Println(runtime.GOMAXPROCS(runtime.NumCPU() - 1))
var port int
var multicore, reuseport bool
// Example command: go run echo.go --port 9000 --multicore=true --reuseport=true
flag.IntVar(&port, "port", 9000, "--port 9000")
flag.BoolVar(&multicore, "multicore", false, "--multicore true")
flag.BoolVar(&reuseport, "reuseport", false, "--reuseport true")
flag.Parse()
echo := new(echoServer)
log.Fatal(gnet.Run(echo, fmt.Sprintf("udp://:%d", port), gnet.WithMulticore(multicore), gnet.WithReusePort(reuseport)))
}
Then this might be caused by Thundering Herd, now that we have a clue about the issue, I'll try to dive deep and submit a fix, thanks for catching this.
Thanks for your hard and brilliant work. As a beginner in Go, I have learned a lot from your articles and code.
Would this issue reproduce on Linux? @czy0538
@panjf2000 My personal Linux server doesn't have this issue, but it only has one core, which may affect the test.
I found that this issue is related to the gnet.WithMulticore(multicore) option. When this option is set to true, this issue occurs, and the number of duplicated messages is equal to the number of CPU cores I have, but it is not affected by GOMAXPROCS.
I tested it using the simplest echo program, and here are the test results:
❯ GOMAXPROCS=1 go run ./echo.go --multicore=false 1 Hello world ^Csignal: interrupt ❯ GOMAXPROCS=1 go run ./echo.go --multicore=true 1 Hello world Hello world Hello world Hello world Hello world Hello world Hello world Hello world ^Csignal: interrupt ❯ go run ./echo.go --multicore=true 8 Hello world Hello world Hello world Hello world Hello world Hello world Hello world Hello world ^Csignal: interrupt
Here are my test code:
package main import ( "flag" "fmt" "log" "runtime" "github.com/panjf2000/gnet/v2" ) type echoServer struct { gnet.BuiltinEventEngine } func (es *echoServer) OnTraffic(c gnet.Conn) gnet.Action { data, _ := c.Next(-1) newData := make([]byte, len(data)) copy(newData, data) go func(data []byte) { fmt.Println(string(data)) }(newData) // c.Write(data) return gnet.None } func main() { fmt.Println(runtime.GOMAXPROCS(runtime.NumCPU() - 1)) var port int var multicore, reuseport bool // Example command: go run echo.go --port 9000 --multicore=true --reuseport=true flag.IntVar(&port, "port", 9000, "--port 9000") flag.BoolVar(&multicore, "multicore", false, "--multicore true") flag.BoolVar(&reuseport, "reuseport", false, "--reuseport true") flag.Parse() echo := new(echoServer) log.Fatal(gnet.Run(echo, fmt.Sprintf("udp://:%d", port), gnet.WithMulticore(multicore), gnet.WithReusePort(reuseport))) }
Please also share the code of client, I'm trying to reproduce this issue on my multi-cores linux server, thanks! @czy0538
I found that this issue is related to the gnet.WithMulticore(multicore) option. When this option is set to true, this issue occurs, and the number of duplicated messages is equal to the number of CPU cores I have, but it is not affected by GOMAXPROCS. I tested it using the simplest echo program, and here are the test results:
❯ GOMAXPROCS=1 go run ./echo.go --multicore=false 1 Hello world ^Csignal: interrupt ❯ GOMAXPROCS=1 go run ./echo.go --multicore=true 1 Hello world Hello world Hello world Hello world Hello world Hello world Hello world Hello world ^Csignal: interrupt ❯ go run ./echo.go --multicore=true 8 Hello world Hello world Hello world Hello world Hello world Hello world Hello world Hello world ^Csignal: interrupt
Here are my test code:
package main import ( "flag" "fmt" "log" "runtime" "github.com/panjf2000/gnet/v2" ) type echoServer struct { gnet.BuiltinEventEngine } func (es *echoServer) OnTraffic(c gnet.Conn) gnet.Action { data, _ := c.Next(-1) newData := make([]byte, len(data)) copy(newData, data) go func(data []byte) { fmt.Println(string(data)) }(newData) // c.Write(data) return gnet.None } func main() { fmt.Println(runtime.GOMAXPROCS(runtime.NumCPU() - 1)) var port int var multicore, reuseport bool // Example command: go run echo.go --port 9000 --multicore=true --reuseport=true flag.IntVar(&port, "port", 9000, "--port 9000") flag.BoolVar(&multicore, "multicore", false, "--multicore true") flag.BoolVar(&reuseport, "reuseport", false, "--reuseport true") flag.Parse() echo := new(echoServer) log.Fatal(gnet.Run(echo, fmt.Sprintf("udp://:%d", port), gnet.WithMulticore(multicore), gnet.WithReusePort(reuseport))) }
Please also share the code of client, I'm trying to reproduce this issue on my multi-cores linux server, thanks! @czy0538
I tried both a Python script and a simple Go program, and here is the code.
import socket
# 目标IP地址和端口
UDP_IP = "255.255.255.255"
UDP_PORT = 9000
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
MESSAGE = b"Hello world"
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
sock.close()
@panjf2000
I think this issue is not caused by thunder herd but by SO_BROADCAST
, gnet starts multiple listeners with reuseport flag when multicore=true
or the protocol is UDP and UDP broadcast will send the frame to all listeners, which results in multiple received messages. The reason why the std net
didn't encounter this issue is because you only start one listener for it, you'll observe the same phenomenon in this issue if you start more than one UDP listener with reuseport flag.
I think this issue is not caused by thunder herd but by
SO_BROADCAST
, gnet starts multiple listeners with reuseport flag whenmulticore=true
or the protocol is UDP and UDP broadcast will send the frame to all listeners, which results in multiple received messages. The reason why the stdnet
didn't encounter this issue is because you only start one listener for it, you'll observe the same phenomenon in this issue if you start more than one UDP listener with reuseport flag.
Ah, I see! Thanks for your answer. It seems that I need to set multicore = false
when facing broadcasting, or add some filtering at the application layer. Thanks again for your explanation.
Update 2023-02-27 14:07: I found that this issue is related to the gnet.WithMulticore(multicore) option .https://github.com/panjf2000/gnet/issues/442#issuecomment-1445759063
Questions with details
Short description of the issue:
When sending broadcast packets to a specific port and listening on that port, the gnet server receives multiple duplicate packets, while Wireshark capture, the net package, and Python program only receive one packet.
当发送 UDP 广播包时,gnet server 会重复的收到同一个包,net 、python 不会出现此问题,wireshark 也只会捕获一个包。
Some potentially useful information:
Log
gnet:
wireshark
net
Code snippets (optional)
gnet server
Code for sending UDP broadcast packets
net code