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  }