gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/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  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client"
    11  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector"
    12  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry"
    13  	"gitee.com/liuxuezhan/go-micro-v1.18.0/router"
    14  	pb "gitee.com/liuxuezhan/go-micro-v1.18.0/router/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 &registry.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  }