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  }