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 := ®istryRouter{ 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 }