github.com/annwntech/go-micro/v2@v2.9.5/client/selector/router/router.go (about) 1 // Package router is a network/router selector 2 package router 3 4 import ( 5 "context" 6 "os" 7 "sort" 8 "sync" 9 10 "github.com/annwntech/go-micro/v2/client" 11 "github.com/annwntech/go-micro/v2/client/selector" 12 "github.com/annwntech/go-micro/v2/registry" 13 "github.com/annwntech/go-micro/v2/router" 14 pb "github.com/annwntech/go-micro/v2/router/service/proto" 15 ) 16 17 type routerSelector struct { 18 opts selector.Options 19 20 // the router 21 r router.Router 22 23 // the client we have 24 c client.Client 25 26 // the client for the remote router 27 rs pb.RouterService 28 29 // name of the router 30 name string 31 32 // address of the remote router 33 addr string 34 35 // whether to use the remote router 36 remote bool 37 } 38 39 type clientKey struct{} 40 type routerKey struct{} 41 42 // getRoutes returns the routes whether they are remote or local 43 func (r *routerSelector) getRoutes(service string) ([]router.Route, error) { 44 if !r.remote { 45 // lookup router for routes for the service 46 return r.r.Lookup( 47 router.QueryService(service), 48 ) 49 } 50 51 // lookup the remote router 52 53 var addrs []string 54 55 // set the remote address if specified 56 if len(r.addr) > 0 { 57 addrs = append(addrs, r.addr) 58 } else { 59 // we have a name so we need to check the registry 60 services, err := r.c.Options().Registry.GetService(r.name) 61 if err != nil { 62 return nil, err 63 } 64 65 for _, service := range services { 66 for _, node := range service.Nodes { 67 addrs = append(addrs, node.Address) 68 } 69 } 70 } 71 72 // no router addresses available 73 if len(addrs) == 0 { 74 return nil, selector.ErrNoneAvailable 75 } 76 77 var pbRoutes *pb.LookupResponse 78 var err error 79 80 // TODO: implement backoff and retries 81 for _, addr := range addrs { 82 // call the router 83 pbRoutes, err = r.rs.Lookup(context.Background(), &pb.LookupRequest{ 84 Query: &pb.Query{ 85 Service: service, 86 }, 87 }, client.WithAddress(addr)) 88 if err != nil { 89 continue 90 } 91 break 92 } 93 94 // errored out 95 if err != nil { 96 return nil, err 97 } 98 99 // no routes 100 if pbRoutes == nil { 101 return nil, selector.ErrNoneAvailable 102 } 103 104 routes := make([]router.Route, 0, len(pbRoutes.Routes)) 105 106 // convert from pb to []*router.Route 107 for _, r := range pbRoutes.Routes { 108 routes = append(routes, router.Route{ 109 Service: r.Service, 110 Address: r.Address, 111 Gateway: r.Gateway, 112 Network: r.Network, 113 Link: r.Link, 114 Metric: r.Metric, 115 }) 116 } 117 118 return routes, nil 119 } 120 121 func (r *routerSelector) Init(opts ...selector.Option) error { 122 // no op 123 return nil 124 } 125 126 func (r *routerSelector) Options() selector.Options { 127 return r.opts 128 } 129 130 func (r *routerSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { 131 // TODO: pull routes asynchronously and cache 132 routes, err := r.getRoutes(service) 133 if err != nil { 134 return nil, err 135 } 136 137 // no routes return not found error 138 if len(routes) == 0 { 139 return nil, selector.ErrNotFound 140 } 141 142 // TODO: apply filters by pseudo constructing service 143 144 // sort the routes based on metric 145 sort.Slice(routes, func(i, j int) bool { 146 return routes[i].Metric < routes[j].Metric 147 }) 148 149 // roundrobin assuming routes are in metric preference order 150 var i int 151 var mtx sync.Mutex 152 153 return func() (*registry.Node, error) { 154 // get index and increment counter with every call to next 155 mtx.Lock() 156 idx := i 157 i++ 158 mtx.Unlock() 159 160 // get route based on idx 161 route := routes[idx%len(routes)] 162 163 // defaults to gateway and no port 164 address := route.Address 165 if len(route.Gateway) > 0 { 166 address = route.Gateway 167 } 168 169 // return as a node 170 return ®istry.Node{ 171 // TODO: add id and metadata if we can 172 Address: address, 173 }, nil 174 }, nil 175 } 176 177 func (r *routerSelector) Mark(service string, node *registry.Node, err error) { 178 // TODO: pass back metrics or information to the router 179 } 180 181 func (r *routerSelector) Reset(service string) { 182 // TODO: reset the metrics or information at the router 183 } 184 185 func (r *routerSelector) Close() error { 186 // stop the router advertisements 187 return r.r.Stop() 188 } 189 190 func (r *routerSelector) String() string { 191 return "router" 192 } 193 194 // NewSelector returns a new router based selector 195 func NewSelector(opts ...selector.Option) selector.Selector { 196 options := selector.Options{ 197 Context: context.Background(), 198 } 199 200 for _, o := range opts { 201 o(&options) 202 } 203 204 // set default registry if not set 205 if options.Registry == nil { 206 options.Registry = registry.DefaultRegistry 207 } 208 209 // try get router from the context 210 r, ok := options.Context.Value(routerKey{}).(router.Router) 211 if !ok { 212 // TODO: Use router.DefaultRouter? 213 r = router.NewRouter( 214 router.Registry(options.Registry), 215 ) 216 } 217 218 // try get client from the context 219 c, ok := options.Context.Value(clientKey{}).(client.Client) 220 if !ok { 221 c = client.DefaultClient 222 } 223 224 // get the router from env vars if its a remote service 225 remote := true 226 routerName := os.Getenv("MICRO_ROUTER") 227 routerAddress := os.Getenv("MICRO_ROUTER_ADDRESS") 228 229 // start the router advertisements if we're running it locally 230 if len(routerName) == 0 && len(routerAddress) == 0 { 231 go r.Advertise() 232 remote = false 233 } 234 235 return &routerSelector{ 236 opts: options, 237 // set the internal router 238 r: r, 239 // set the client 240 c: c, 241 // set the router client 242 rs: pb.NewRouterService(routerName, c), 243 // name of the router 244 name: routerName, 245 // address of router 246 addr: routerAddress, 247 // let ourselves know to use the remote router 248 remote: remote, 249 } 250 } 251 252 // WithClient sets the client for the request 253 func WithClient(c client.Client) selector.Option { 254 return func(o *selector.Options) { 255 if o.Context == nil { 256 o.Context = context.Background() 257 } 258 o.Context = context.WithValue(o.Context, clientKey{}, c) 259 } 260 } 261 262 // WithRouter sets the router as an option 263 func WithRouter(r router.Router) selector.Option { 264 return func(o *selector.Options) { 265 if o.Context == nil { 266 o.Context = context.Background() 267 } 268 o.Context = context.WithValue(o.Context, routerKey{}, r) 269 } 270 }