gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/api/router/registry/registry.go (about)

     1  // Package registry provides a dynamic api service router
     2  package registry
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"net/http"
     9  	"regexp"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	"gitee.com/liuxuezhan/go-micro-v1.18.0/api"
    15  	"gitee.com/liuxuezhan/go-micro-v1.18.0/api/router"
    16  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry"
    17  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry/cache"
    18  )
    19  
    20  // router is the default router
    21  type registryRouter struct {
    22  	exit chan bool
    23  	opts router.Options
    24  
    25  	// registry cache
    26  	rc cache.Cache
    27  
    28  	sync.RWMutex
    29  	eps map[string]*api.Service
    30  }
    31  
    32  func setNamespace(ns, name string) string {
    33  	ns = strings.TrimSpace(ns)
    34  	name = strings.TrimSpace(name)
    35  
    36  	// no namespace
    37  	if len(ns) == 0 {
    38  		return name
    39  	}
    40  
    41  	switch {
    42  	// has - suffix
    43  	case strings.HasSuffix(ns, "-"):
    44  		return strings.Replace(ns+name, ".", "-", -1)
    45  	// has . suffix
    46  	case strings.HasSuffix(ns, "."):
    47  		return ns + name
    48  	}
    49  
    50  	// default join .
    51  	return strings.Join([]string{ns, name}, ".")
    52  }
    53  
    54  func (r *registryRouter) isClosed() bool {
    55  	select {
    56  	case <-r.exit:
    57  		return true
    58  	default:
    59  		return false
    60  	}
    61  }
    62  
    63  // refresh list of api services
    64  func (r *registryRouter) refresh() {
    65  	var attempts int
    66  
    67  	for {
    68  		services, err := r.opts.Registry.ListServices()
    69  		if err != nil {
    70  			attempts++
    71  			log.Println("Error listing endpoints", err)
    72  			time.Sleep(time.Duration(attempts) * time.Second)
    73  			continue
    74  		}
    75  
    76  		attempts = 0
    77  
    78  		// for each service, get service and store endpoints
    79  		for _, s := range services {
    80  			// only get services for this namespace
    81  			if !strings.HasPrefix(s.Name, r.opts.Namespace) {
    82  				continue
    83  			}
    84  			service, err := r.rc.GetService(s.Name)
    85  			if err != nil {
    86  				continue
    87  			}
    88  			r.store(service)
    89  		}
    90  
    91  		// refresh list in 10 minutes... cruft
    92  		select {
    93  		case <-time.After(time.Minute * 10):
    94  		case <-r.exit:
    95  			return
    96  		}
    97  	}
    98  }
    99  
   100  // process watch event
   101  func (r *registryRouter) process(res *registry.Result) {
   102  	// skip these things
   103  	if res == nil || res.Service == nil || !strings.HasPrefix(res.Service.Name, r.opts.Namespace) {
   104  		return
   105  	}
   106  
   107  	// get entry from cache
   108  	service, err := r.rc.GetService(res.Service.Name)
   109  	if err != nil {
   110  		return
   111  	}
   112  
   113  	// update our local endpoints
   114  	r.store(service)
   115  }
   116  
   117  // store local endpoint cache
   118  func (r *registryRouter) store(services []*registry.Service) {
   119  	// endpoints
   120  	eps := map[string]*api.Service{}
   121  
   122  	// services
   123  	names := map[string]bool{}
   124  
   125  	// create a new endpoint mapping
   126  	for _, service := range services {
   127  		// set names we need later
   128  		names[service.Name] = true
   129  
   130  		// map per endpoint
   131  		for _, endpoint := range service.Endpoints {
   132  			// create a key service:endpoint_name
   133  			key := fmt.Sprintf("%s:%s", service.Name, endpoint.Name)
   134  			// decode endpoint
   135  			end := api.Decode(endpoint.Metadata)
   136  
   137  			// if we got nothing skip
   138  			if err := api.Validate(end); err != nil {
   139  				continue
   140  			}
   141  
   142  			// try get endpoint
   143  			ep, ok := eps[key]
   144  			if !ok {
   145  				ep = &api.Service{Name: service.Name}
   146  			}
   147  
   148  			// overwrite the endpoint
   149  			ep.Endpoint = end
   150  			// append services
   151  			ep.Services = append(ep.Services, service)
   152  			// store it
   153  			eps[key] = ep
   154  		}
   155  	}
   156  
   157  	r.Lock()
   158  	defer r.Unlock()
   159  
   160  	// delete any existing eps for services we know
   161  	for key, service := range r.eps {
   162  		// skip what we don't care about
   163  		if !names[service.Name] {
   164  			continue
   165  		}
   166  
   167  		// ok we know this thing
   168  		// delete delete delete
   169  		delete(r.eps, key)
   170  	}
   171  
   172  	// now set the eps we have
   173  	for name, endpoint := range eps {
   174  		r.eps[name] = endpoint
   175  	}
   176  }
   177  
   178  // watch for endpoint changes
   179  func (r *registryRouter) watch() {
   180  	var attempts int
   181  
   182  	for {
   183  		if r.isClosed() {
   184  			return
   185  		}
   186  
   187  		// watch for changes
   188  		w, err := r.opts.Registry.Watch()
   189  		if err != nil {
   190  			attempts++
   191  			log.Println("Error watching endpoints", err)
   192  			time.Sleep(time.Duration(attempts) * time.Second)
   193  			continue
   194  		}
   195  
   196  		ch := make(chan bool)
   197  
   198  		go func() {
   199  			select {
   200  			case <-ch:
   201  				w.Stop()
   202  			case <-r.exit:
   203  				w.Stop()
   204  			}
   205  		}()
   206  
   207  		// reset if we get here
   208  		attempts = 0
   209  
   210  		for {
   211  			// process next event
   212  			res, err := w.Next()
   213  			if err != nil {
   214  				log.Println("Error getting next endpoint", err)
   215  				close(ch)
   216  				break
   217  			}
   218  			r.process(res)
   219  		}
   220  	}
   221  }
   222  
   223  func (r *registryRouter) Options() router.Options {
   224  	return r.opts
   225  }
   226  
   227  func (r *registryRouter) Close() error {
   228  	select {
   229  	case <-r.exit:
   230  		return nil
   231  	default:
   232  		close(r.exit)
   233  		r.rc.Stop()
   234  	}
   235  	return nil
   236  }
   237  
   238  func (r *registryRouter) Endpoint(req *http.Request) (*api.Service, error) {
   239  	if r.isClosed() {
   240  		return nil, errors.New("router closed")
   241  	}
   242  
   243  	r.RLock()
   244  	defer r.RUnlock()
   245  
   246  	// use the first match
   247  	// TODO: weighted matching
   248  	for _, e := range r.eps {
   249  		ep := e.Endpoint
   250  
   251  		// match
   252  		var pathMatch, hostMatch, methodMatch bool
   253  
   254  		// 1. try method GET, POST, PUT, etc
   255  		// 2. try host example.com, foobar.com, etc
   256  		// 3. try path /foo/bar, /bar/baz, etc
   257  
   258  		// 1. try match method
   259  		for _, m := range ep.Method {
   260  			if req.Method == m {
   261  				methodMatch = true
   262  				break
   263  			}
   264  		}
   265  
   266  		// no match on method pass
   267  		if len(ep.Method) > 0 && !methodMatch {
   268  			continue
   269  		}
   270  
   271  		// 2. try match host
   272  		for _, h := range ep.Host {
   273  			if req.Host == h {
   274  				hostMatch = true
   275  				break
   276  			}
   277  		}
   278  
   279  		// no match on host pass
   280  		if len(ep.Host) > 0 && !hostMatch {
   281  			continue
   282  		}
   283  
   284  		// 3. try match paths
   285  		for _, p := range ep.Path {
   286  			re, err := regexp.CompilePOSIX(p)
   287  			if err == nil && re.MatchString(req.URL.Path) {
   288  				pathMatch = true
   289  				break
   290  			}
   291  		}
   292  
   293  		// no match pass
   294  		if len(ep.Path) > 0 && !pathMatch {
   295  			continue
   296  		}
   297  
   298  		// TODO: Percentage traffic
   299  
   300  		// we got here, so its a match
   301  		return e, nil
   302  	}
   303  
   304  	// no match
   305  	return nil, errors.New("not found")
   306  }
   307  
   308  func (r *registryRouter) Route(req *http.Request) (*api.Service, error) {
   309  	if r.isClosed() {
   310  		return nil, errors.New("router closed")
   311  	}
   312  
   313  	// try get an endpoint
   314  	ep, err := r.Endpoint(req)
   315  	if err == nil {
   316  		return ep, nil
   317  	}
   318  
   319  	// error not nil
   320  	// ignore that shit
   321  	// TODO: don't ignore that shit
   322  
   323  	// get the service name
   324  	rp, err := r.opts.Resolver.Resolve(req)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	// service name
   330  	name := setNamespace(r.opts.Namespace, rp.Name)
   331  
   332  	// get service
   333  	services, err := r.rc.GetService(name)
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  
   338  	// only use endpoint matching when the meta handler is set aka api.Default
   339  	switch r.opts.Handler {
   340  	// rpc handlers
   341  	case "meta", "api", "rpc":
   342  		handler := r.opts.Handler
   343  
   344  		// set default handler to api
   345  		if r.opts.Handler == "meta" {
   346  			handler = "rpc"
   347  		}
   348  
   349  		// construct api service
   350  		return &api.Service{
   351  			Name: name,
   352  			Endpoint: &api.Endpoint{
   353  				Name:    rp.Method,
   354  				Handler: handler,
   355  			},
   356  			Services: services,
   357  		}, nil
   358  	// http handler
   359  	case "http", "proxy", "web":
   360  		// construct api service
   361  		return &api.Service{
   362  			Name: name,
   363  			Endpoint: &api.Endpoint{
   364  				Name:    req.URL.String(),
   365  				Handler: r.opts.Handler,
   366  				Host:    []string{req.Host},
   367  				Method:  []string{req.Method},
   368  				Path:    []string{req.URL.Path},
   369  			},
   370  			Services: services,
   371  		}, nil
   372  	}
   373  
   374  	return nil, errors.New("unknown handler")
   375  }
   376  
   377  func newRouter(opts ...router.Option) *registryRouter {
   378  	options := router.NewOptions(opts...)
   379  	r := &registryRouter{
   380  		exit: make(chan bool),
   381  		opts: options,
   382  		rc:   cache.New(options.Registry),
   383  		eps:  make(map[string]*api.Service),
   384  	}
   385  	go r.watch()
   386  	go r.refresh()
   387  	return r
   388  }
   389  
   390  // NewRouter returns the default router
   391  func NewRouter(opts ...router.Option) router.Router {
   392  	return newRouter(opts...)
   393  }