github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/examples/cli/main.go (about) 1 //nolint:gochecknoinits,forbidigo,goerr113,forcetypeassert,wrapcheck 2 package main 3 4 import ( 5 "fmt" 6 "log" 7 "net/http" 8 _ "net/http/pprof" //nolint:gosec 9 "os" 10 "os/signal" 11 "runtime" 12 "sync/atomic" 13 "time" 14 15 "github.com/pawelgaczynski/gain" 16 gainNet "github.com/pawelgaczynski/gain/pkg/net" 17 "github.com/rs/zerolog" 18 "github.com/urfave/cli/v2" 19 ) 20 21 const ( 22 port = 9000 23 ) 24 25 var ( 26 goMaxProcs = runtime.NumCPU() * 2 27 pprofReadHeaderTimeout = 3 * time.Second 28 ) 29 30 var architectures = []string{ 31 "reactor", "sharding", 32 } 33 34 var allowedNetworks = []string{ 35 gainNet.TCP, gainNet.UDP, 36 } 37 38 var handlers = []string{ 39 "http", "echo", 40 } 41 42 type cmdConfig struct { 43 gain.Config 44 port int 45 handler string 46 network string 47 } 48 49 var contains = func(s []string, str string) bool { 50 for _, v := range s { 51 if v == str { 52 return true 53 } 54 } 55 56 return false 57 } 58 59 var config = &cmdConfig{Config: gain.NewConfig()} 60 61 var flags = []cli.Flag{ 62 &cli.StringFlag{ 63 Name: "architecture", 64 Value: "reactor", 65 Usage: "server architecture", 66 Action: func(ctx *cli.Context, v string) error { 67 if !contains(architectures, v) { 68 return fmt.Errorf("possible values for architectures: %v", architectures) 69 } 70 switch v { 71 case "reactor": 72 config.Architecture = gain.Reactor 73 case "sharding": 74 config.Architecture = gain.SocketSharding 75 } 76 77 return nil 78 }, 79 }, 80 &cli.IntFlag{ 81 Name: "workers", 82 Value: runtime.NumCPU(), 83 Usage: "number of workers", 84 Destination: &config.Workers, 85 }, 86 &cli.IntFlag{ 87 Name: "port", 88 Value: port, 89 Usage: "listen port", 90 Destination: &config.port, 91 }, 92 &cli.BoolFlag{ 93 Name: "prettyLogger", 94 Value: false, 95 Usage: "print prettier logs. Warning: it can slow down server", 96 Destination: &config.PrettyLogger, 97 }, 98 &cli.BoolFlag{ 99 Name: "asyncHandler", 100 Value: false, 101 Usage: "use async handler for writes", 102 Destination: &config.AsyncHandler, 103 }, 104 &cli.BoolFlag{ 105 Name: "goroutinePool", 106 Value: false, 107 Usage: "use goroutine pool for async handler", 108 Destination: &config.GoroutinePool, 109 }, 110 &cli.BoolFlag{ 111 Name: "cpuAffinity", 112 Value: false, 113 Usage: "set CPU Affinity", 114 Destination: &config.CPUAffinity, 115 }, 116 &cli.BoolFlag{ 117 Name: "cbpf", 118 Value: false, 119 Usage: "use CBPF for bette packet locality", 120 Destination: &config.CBPFilter, 121 }, 122 &cli.BoolFlag{ 123 Name: "processPriority", 124 Value: false, 125 Usage: "set high process priority. Note: requires root privileges", 126 Destination: &config.ProcessPriority, 127 }, 128 &cli.StringFlag{ 129 Name: "loggerLevel", 130 Value: "error", 131 Usage: "logger level", 132 Action: func(ctx *cli.Context, v string) error { 133 var err error 134 config.LoggerLevel, err = zerolog.ParseLevel(v) 135 136 return err 137 }, 138 }, 139 &cli.StringFlag{ 140 Name: "handler", 141 Value: "echo", 142 Usage: "handler type. Allowed values are 'http' and 'echo'", 143 Destination: &config.handler, 144 Action: func(ctx *cli.Context, v string) error { 145 if !contains(handlers, v) { 146 return fmt.Errorf("possible values for handler: %v", handlers) 147 } 148 149 return nil 150 }, 151 }, 152 &cli.StringFlag{ 153 Name: "network", 154 Value: gainNet.TCP, 155 Usage: "network type", 156 Destination: &config.network, 157 Action: func(ctx *cli.Context, v string) error { 158 if !contains(allowedNetworks, v) { 159 return fmt.Errorf("possible values for networks: %v", allowedNetworks) 160 } 161 162 return nil 163 }, 164 }, 165 } 166 167 func getHTTPLastLine() []byte { 168 return []byte("\r\nContent-Length: 13\r\n\r\nHello, World!") 169 } 170 171 var ( 172 httpFirstLine = []byte("HTTP/1.1 200 OK\r\nServer: gain\r\nContent-Type: text/plain\r\nDate: ") 173 httpLastLine = getHTTPLastLine() 174 ) 175 176 var now atomic.Value 177 178 func ticktock() { 179 now.Store(nowTimeFormat()) 180 181 for range time.Tick(time.Second) { 182 now.Store(nowTimeFormat()) 183 } 184 } 185 186 func NowTimeFormat() string { 187 return now.Load().(string) 188 } 189 190 func nowTimeFormat() string { 191 return time.Now().Format("Mon, 02 Jan 2006 15:04:05 GMT") 192 } 193 194 type EchoHandler struct { 195 gain.DefaultEventHandler 196 } 197 198 func (h EchoHandler) OnRead(c gain.Conn, n int) { 199 buf, _ := c.Next(n) 200 _, _ = c.Write(buf) 201 } 202 203 type HTTPHandler struct { 204 gain.DefaultEventHandler 205 } 206 207 func (h HTTPHandler) OnRead(conn gain.Conn, n int) { 208 _, err := conn.Discard(n) 209 if err != nil { 210 log.Panic(err) 211 } 212 213 _, err = conn.Write(httpFirstLine) 214 if err != nil { 215 log.Panic(err) 216 } 217 218 _, err = conn.Write([]byte(NowTimeFormat())) 219 if err != nil { 220 log.Panic(err) 221 } 222 223 _, err = conn.Write(httpLastLine) 224 if err != nil { 225 log.Panic(err) 226 } 227 } 228 229 func main() { 230 runtime.GOMAXPROCS(goMaxProcs) 231 232 go func() { 233 server := &http.Server{ 234 Addr: "0.0.0.0:6061", 235 ReadHeaderTimeout: pprofReadHeaderTimeout, 236 } 237 log.Println(server.ListenAndServe()) 238 }() 239 240 var server gain.Server 241 app := &cli.App{ 242 EnableBashCompletion: true, 243 Flags: flags, 244 Name: "cli", 245 Usage: "Gain TCP/UDP server", 246 Action: func(*cli.Context) error { 247 fmt.Printf("Configuration: %+v \n", config) 248 fmt.Printf("Starting Gain %s Server...\n", config.network) 249 if config.handler == "echo" { 250 server = gain.NewServer(EchoHandler{}, config.Config) 251 } else if config.handler == "http" { 252 server = gain.NewServer(HTTPHandler{}, config.Config) 253 } 254 255 return server.Start(fmt.Sprintf("%s://0.0.0.0:%d", config.network, port)) 256 }, 257 } 258 259 c := make(chan os.Signal, 1) 260 signal.Notify(c, os.Interrupt) 261 262 go func() { 263 <-c 264 fmt.Println("Server closing...") 265 server.Shutdown() 266 }() 267 268 if err := app.Run(os.Args); err != nil { 269 log.Panic(err) 270 } 271 } 272 273 func init() { 274 now.Store(nowTimeFormat()) 275 276 go ticktock() 277 }