github.com/godevsig/adaptiveservice@v0.9.23/client.go (about)

     1  package adaptiveservice
     2  
     3  import (
     4  	"math/rand"
     5  	"strings"
     6  	"time"
     7  )
     8  
     9  // Client uses services.
    10  type Client struct {
    11  	*conf
    12  	discoverTimeout int // in seconds
    13  	deepCopy        bool
    14  }
    15  
    16  // NewClient creates a client which discovers services.
    17  func NewClient(options ...Option) *Client {
    18  	c := &Client{
    19  		conf:            newConf(),
    20  		discoverTimeout: -1,
    21  	}
    22  
    23  	for _, o := range options {
    24  		o(c.conf)
    25  	}
    26  
    27  	c.lg.Debugf("new client created")
    28  	return c
    29  }
    30  
    31  // Discover discovers the wanted service and returns the connection channel,
    32  // from which user can get one or more connections.
    33  // Each connection represents a connection to one of the service providers
    34  // providing the wanted micro service.
    35  //
    36  // Only one service(identified by publisher name and service name) can exist in
    37  // ScopeProcess and ScopeOS, but in ScopeLAN and ScopeWAN there can be many systems
    38  // providing the same service, each systeam(called provider) has an unique provider ID.
    39  //
    40  // Use providerIDs to select target providers in ScopeLAN or ScopeWAN,
    41  // if no provider id presents, discover searches scopes by distance that is
    42  // in the order of ScopeProcess, ScopeOS, ScopeLAN, ScopeWAN, and returns
    43  // only one connection towards the found service which may have been randomly selected
    44  // if more than one services were found.
    45  //
    46  // If any of publisher or service or provider ids contains "*", discover will return
    47  // all currently available connections of the wanted service(s). Make sure to close
    48  // ALL the connections it returns after use.
    49  func (c *Client) Discover(publisher, service string, providerIDs ...string) <-chan Connection {
    50  	connections := make(chan Connection)
    51  
    52  	has := func(target string) bool {
    53  		if len(providerIDs) == 0 { // match all
    54  			return true
    55  		}
    56  		if len(target) == 0 {
    57  			return false
    58  		}
    59  		for _, str := range providerIDs {
    60  			if str == target {
    61  				return true
    62  			}
    63  		}
    64  		return false
    65  	}
    66  
    67  	expect := 1
    68  	if len(providerIDs) != 0 {
    69  		expect = len(providerIDs)
    70  		if has("*") {
    71  			expect = -1 // all
    72  			providerIDs = nil
    73  		}
    74  		if len(c.providerID) == 0 {
    75  			providerID, err := discoverProviderID(c.lg)
    76  			if err != nil {
    77  				providerID = "NA"
    78  			}
    79  			c.providerID = providerID
    80  		}
    81  	}
    82  	if strings.Contains(publisher+service, "*") {
    83  		expect = -1
    84  	}
    85  
    86  	findWithinOS := func() (found int) {
    87  		if !has(c.providerID) {
    88  			return 0
    89  		}
    90  		if c.scope&ScopeProcess == ScopeProcess {
    91  			c.lg.Debugf("finding %s_%s in ScopeProcess", publisher, service)
    92  			ccts := c.lookupServiceChan(publisher, service)
    93  			for _, cct := range ccts {
    94  				connections <- cct.newConnection()
    95  				c.lg.Debugf("channel transport connected")
    96  				found++
    97  				if found == expect {
    98  					return
    99  				}
   100  			}
   101  		}
   102  		if c.scope&ScopeOS == ScopeOS {
   103  			c.lg.Debugf("finding %s_%s in ScopeOS", publisher, service)
   104  			addrs := lookupServiceUDS(publisher, service)
   105  			for _, addr := range addrs {
   106  				conn, err := c.newUDSConnection(addr)
   107  				if err != nil {
   108  					c.lg.Errorf("dial " + addr + " failed")
   109  				} else {
   110  					connections <- conn
   111  					c.lg.Debugf("unix domain socket connected to: %s", addr)
   112  					found++
   113  					if found == expect {
   114  						return
   115  					}
   116  				}
   117  			}
   118  		}
   119  		return
   120  	}
   121  
   122  	findNetwork := func(expect int) (found int) {
   123  		var addrs []string
   124  
   125  		connect := func() {
   126  			for len(addrs) != 0 && found != expect {
   127  				i := rand.Intn(len(addrs))
   128  				addr := addrs[i]
   129  				addrs[i] = addrs[len(addrs)-1]
   130  				addrs = addrs[:len(addrs)-1]
   131  
   132  				conn, err := c.newTCPConnection(addr)
   133  				if err != nil {
   134  					c.lg.Warnf("dial " + addr + " failed")
   135  				} else {
   136  					connections <- conn
   137  					c.lg.Debugf("tcp socket connected to: %s", addr)
   138  					found++
   139  				}
   140  			}
   141  		}
   142  
   143  		if found != expect && c.scope&ScopeLAN == ScopeLAN {
   144  			c.lg.Debugf("finding %s_%s in ScopeLAN", publisher, service)
   145  			addrs = c.lookupServiceLAN(publisher, service, providerIDs...)
   146  			connect()
   147  		}
   148  		if found != expect && c.scope&ScopeWAN == ScopeWAN {
   149  			c.lg.Debugf("finding %s_%s in ScopeWAN", publisher, service)
   150  			if len(c.registryAddr) == 0 {
   151  				addr, err := discoverRegistryAddr(c.lg)
   152  				if err != nil {
   153  					addr = "NA"
   154  				}
   155  				c.registryAddr = addr
   156  				c.lg.Debugf("discovered registry address: %s", addr)
   157  			}
   158  			if c.registryAddr != "NA" {
   159  				addrs = c.lookupServiceWAN(publisher, service, providerIDs...)
   160  				connect()
   161  			}
   162  		}
   163  		return found
   164  	}
   165  
   166  	go func() {
   167  		defer close(connections)
   168  		found := 0
   169  		timeout := c.discoverTimeout
   170  		for found == 0 {
   171  			if found += findWithinOS(); found == expect {
   172  				break
   173  			}
   174  			if found += findNetwork(expect - found); found == expect {
   175  				break
   176  			}
   177  			if timeout == 0 {
   178  				break
   179  			}
   180  			c.lg.Debugf("waiting for service: %s_%s", publisher, service)
   181  			time.Sleep(time.Second)
   182  			timeout--
   183  		}
   184  	}()
   185  
   186  	return connections
   187  }