nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/test/scale_test.go (about)

     1  // +build !race
     2  // Copyright 2018 The Mangos Authors
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use file except in compliance with the License.
     6  // You may obtain a copy of the license at
     7  //
     8  //    http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package test
    17  
    18  // This simple test just fires off a crapton of inproc clients, to see
    19  // how many connections we could handle.  We do this using inproc, because
    20  // we would absolutely exhaust TCP ports before we would hit any of the
    21  // natural limits.  The inproc transport has no such limits, so we are
    22  // effectively just testing goroutine scalability, which is what we want.
    23  // The intention is to demonstrate that mangos can address the C10K problem
    24  // without breaking a sweat.
    25  
    26  import (
    27  	"errors"
    28  	"math/rand"
    29  	"sync"
    30  	"testing"
    31  	"time"
    32  
    33  	"nanomsg.org/go/mangos/v2"
    34  	"nanomsg.org/go/mangos/v2/protocol/rep"
    35  	"nanomsg.org/go/mangos/v2/protocol/req"
    36  	_ "nanomsg.org/go/mangos/v2/transport/inproc"
    37  )
    38  
    39  func scalabilityClient(errp *error, loops int, wg *sync.WaitGroup) {
    40  	defer wg.Done()
    41  	sock, err := req.NewSocket()
    42  	if err != nil {
    43  		*errp = err
    44  		return
    45  	}
    46  	defer sock.Close()
    47  	if err := sock.Dial("inproc://atscale"); err != nil {
    48  		*errp = err
    49  		return
    50  	}
    51  
    52  	for i := 0; i < loops; i++ {
    53  		// Inject a random sleep to avoid pounding the CPU too hard.
    54  		time.Sleep(time.Duration(rand.Int31n(1000)) * time.Microsecond)
    55  
    56  		msg := mangos.NewMessage(3)
    57  		msg.Body = append(msg.Body, []byte("ping")...)
    58  		if err := sock.SendMsg(msg); err != nil {
    59  			*errp = err
    60  			return
    61  		}
    62  
    63  		if msg, err = sock.RecvMsg(); err != nil {
    64  			*errp = err
    65  			return
    66  		}
    67  		if string(msg.Body) != "pong" {
    68  			*errp = errors.New("response mismatch")
    69  			return
    70  		}
    71  		msg.Free()
    72  	}
    73  }
    74  
    75  func scalabilityServer(sock mangos.Socket) {
    76  	defer sock.Close()
    77  	for {
    78  		msg, e := sock.RecvMsg()
    79  		if e != nil {
    80  			return
    81  		}
    82  		msg.Body = append(msg.Body[:0], []byte("pong")...)
    83  		e = sock.SendMsg(msg)
    84  		if e != nil {
    85  			return
    86  		}
    87  	}
    88  }
    89  
    90  func TestScalability(t *testing.T) {
    91  	// Beyond this things get crazy.
    92  	// Note that the thread count actually indicates that you will
    93  	// have this many client sockets, and an equal number of server
    94  	// side pipes.  On my Mac, 20K leads to around 30 sec to run the
    95  	// program, whereas 10k can run in under 10 sec.  This proves we
    96  	// can handle 10K connections.
    97  	loops := 1
    98  	threads := 10000
    99  
   100  	errs := make([]error, threads)
   101  
   102  	ssock, e := rep.NewSocket()
   103  	if e != nil {
   104  		t.Fatalf("Cannot make server socket: %v", e)
   105  	}
   106  	if e = ssock.Listen("inproc://atscale"); e != nil {
   107  		t.Fatalf("Cannot listen: %v", e)
   108  	}
   109  	time.Sleep(time.Millisecond * 100)
   110  	go scalabilityServer(ssock)
   111  	wg := &sync.WaitGroup{}
   112  	wg.Add(threads)
   113  	for i := 0; i < threads; i++ {
   114  		go scalabilityClient(&errs[i], loops, wg)
   115  	}
   116  
   117  	wg.Wait()
   118  	for i := 0; i < threads; i++ {
   119  		if errs[i] != nil {
   120  			t.Fatalf("Test failed: %v", errs[i])
   121  		}
   122  	}
   123  }