nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/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 "nanomsg.org/go/mangos/v2" 44 "nanomsg.org/go/mangos/v2/protocol/rep" 45 "nanomsg.org/go/mangos/v2/protocol/req" 46 47 // register transports 48 _ "nanomsg.org/go/mangos/v2/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 fmt.Printf("Worker %d: Received request '%s'\n", id, string(msg)) 93 94 // Sleep for a random amount of time to simulate "work" 95 sleepTimeSec = int(maxSleepSec * rand.Float64() * float64(time.Second)) 96 fmt.Printf("Worker %d: Sleeping for %v\n", id, time.Duration(sleepTimeSec)) 97 time.Sleep(time.Duration(sleepTimeSec)) 98 99 response = "Hello, " + string(msg) 100 fmt.Printf("Worker %d: Sending response '%s'\n", id, response) 101 err = ctx.Send([]byte(response)) 102 if err != nil { 103 die("can't send reply: %s", err.Error()) 104 } 105 } 106 } 107 108 // Create several workers that are ready to handle incoming requests. 109 for ii := 0; ii < numWorkers; ii++ { 110 go worker(ii) 111 } 112 113 for { 114 // Sleep so the process does not exit 115 time.Sleep(100 * time.Second) 116 } 117 118 } 119 120 // Create a client that will connect to the REP server and send a message string. Await the response and then quit. 121 func client(url, msg string) { 122 var sock mangos.Socket 123 var err error 124 var response []byte 125 126 // Generate a random ID for the client so we can track them. 127 var id = rand.Intn(9999) 128 129 if sock, err = req.NewSocket(); err != nil { 130 die("can't get new req socket: %s", err.Error()) 131 } 132 if err = sock.Dial(url); err != nil { 133 die("can't dial on req socket: %s", err.Error()) 134 } 135 fmt.Printf("Client %d: Sending message '%s'\n", id, msg) 136 if err = sock.Send([]byte(msg)); err != nil { 137 die("can't send message on push socket: %s", err.Error()) 138 } 139 if response, err = sock.Recv(); err != nil { 140 die("can't receive date: %s", err.Error()) 141 } 142 fmt.Printf("Client %d: Received response '%s'\n", id, string(response)) 143 sock.Close() 144 } 145 146 func main() { 147 rand.Seed(time.Now().UnixNano()) 148 149 if len(os.Args) > 2 && os.Args[1] == "server" { 150 server(os.Args[2]) 151 os.Exit(0) 152 } 153 if len(os.Args) > 2 && os.Args[1] == "client" { 154 client(os.Args[2], os.Args[3]) 155 os.Exit(0) 156 } 157 fmt.Fprintf(os.Stderr, "Usage: context server <URL>\n") 158 fmt.Fprintf(os.Stderr, " context client <URL> <MSG>\n") 159 os.Exit(1) 160 }