go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/examples/context/context.go (about) 1 // Copyright 2018 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // context implements a request/reply server that utilizes a pool of worker goroutines to service 16 // multiple requests simultaneously. Each goroutine has it's own context which keeps track of 17 // which client the request came from so that it can reply to the correct client. 18 // 19 // The server is a listening rep socket, and client is a dialing req socket. 20 // 21 // To use: 22 // 23 // $ go build . 24 // $ url=tcp://127.0.0.1:40899 25 // $ 26 // $ ./context server $url & server=$! && sleep 1 27 // $ ./context client $url "John" 28 // $ ./context client $url "Bill" 29 // $ ./context client $url "Mary" 30 // $ ./context client $url "Susan" 31 // $ ./context client $url "Mark" 32 // 33 // $ kill $server 34 // 35 package main 36 37 import ( 38 "fmt" 39 "math/rand" 40 "os" 41 "time" 42 43 "go.nanomsg.org/mangos/v3" 44 "go.nanomsg.org/mangos/v3/protocol/rep" 45 "go.nanomsg.org/mangos/v3/protocol/req" 46 47 // register transports 48 _ "go.nanomsg.org/mangos/v3/transport/all" 49 ) 50 51 func die(format string, v ...interface{}) { 52 fmt.Fprintln(os.Stderr, fmt.Sprintf(format, v...)) 53 os.Exit(1) 54 } 55 56 /** 57 This function will act as the server. It will create a single REP socket and a pool of worker go routines that will 58 simultaneously service requests from many clients. This means that two clients can both send a request to the 59 server and the server can deal with both of them concurrently. The server does not need to 60 respond in the same order the requests come in. Each worker responds on it's own context. 61 */ 62 func server(url string) { 63 var sock mangos.Socket 64 var err error 65 const numWorkers = 3 66 67 if sock, err = rep.NewSocket(); err != nil { 68 die("can't get new rep socket: %s", err) 69 } 70 if err = sock.Listen(url); err != nil { 71 die("can't listen on rep socket: %s", err.Error()) 72 } 73 74 // Create a worker go routine that will create it's own context on the REP socket. 75 // This context will receive a request that occurs on the socket from one of the clients. 76 // This worker can then reply on the context, which will ensure 77 // that the client of this particular request will get the response. 78 var worker = func(id int) { 79 const maxSleepSec = 1 80 var ctx mangos.Context 81 var err error 82 var sleepTimeSec int 83 var response string 84 var msg []byte 85 86 ctx, err = sock.OpenContext() 87 for { // Loop endlessly so workers do not die. 88 if err != nil { 89 die("can't create context on rep socket: %s", err) 90 } 91 msg, err = ctx.Recv() 92 if err != nil { 93 die("can't receive from context: %s", err) 94 } 95 fmt.Printf("Worker %d: Received request '%s'\n", id, string(msg)) 96 97 // Sleep for a random amount of time to simulate "work" 98 sleepTimeSec = int(maxSleepSec * rand.Float64() * float64(time.Second)) 99 fmt.Printf("Worker %d: Sleeping for %v\n", id, time.Duration(sleepTimeSec)) 100 time.Sleep(time.Duration(sleepTimeSec)) 101 102 response = "Hello, " + string(msg) 103 fmt.Printf("Worker %d: Sending response '%s'\n", id, response) 104 err = ctx.Send([]byte(response)) 105 if err != nil { 106 die("can't send reply: %s", err.Error()) 107 } 108 } 109 } 110 111 // Create several workers that are ready to handle incoming requests. 112 for ii := 0; ii < numWorkers; ii++ { 113 go worker(ii) 114 } 115 116 for { 117 // Sleep so the process does not exit 118 time.Sleep(100 * time.Second) 119 } 120 121 } 122 123 // Create a client that will connect to the REP server and send a message string. Await the response and then quit. 124 func client(url, msg string) { 125 var sock mangos.Socket 126 var err error 127 var response []byte 128 129 // Generate a random ID for the client so we can track them. 130 var id = rand.Intn(9999) 131 132 if sock, err = req.NewSocket(); err != nil { 133 die("can't get new req socket: %s", err.Error()) 134 } 135 if err = sock.Dial(url); err != nil { 136 die("can't dial on req socket: %s", err.Error()) 137 } 138 fmt.Printf("Client %d: Sending message '%s'\n", id, msg) 139 if err = sock.Send([]byte(msg)); err != nil { 140 die("can't send message on push socket: %s", err.Error()) 141 } 142 if response, err = sock.Recv(); err != nil { 143 die("can't receive date: %s", err.Error()) 144 } 145 fmt.Printf("Client %d: Received response '%s'\n", id, string(response)) 146 sock.Close() 147 } 148 149 func main() { 150 rand.Seed(time.Now().UnixNano()) 151 152 if len(os.Args) > 2 && os.Args[1] == "server" { 153 server(os.Args[2]) 154 os.Exit(0) 155 } 156 if len(os.Args) > 2 && os.Args[1] == "client" { 157 client(os.Args[2], os.Args[3]) 158 os.Exit(0) 159 } 160 fmt.Fprintf(os.Stderr, "Usage: context server <URL>\n") 161 fmt.Fprintf(os.Stderr, " context client <URL> <MSG>\n") 162 os.Exit(1) 163 }