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 }