github.com/wangyougui/gf/v2@v2.6.5/net/gclient/gclient_discovery.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gclient 8 9 import ( 10 "context" 11 "net/http" 12 13 "github.com/wangyougui/gf/v2/container/gmap" 14 "github.com/wangyougui/gf/v2/errors/gcode" 15 "github.com/wangyougui/gf/v2/errors/gerror" 16 "github.com/wangyougui/gf/v2/internal/intlog" 17 "github.com/wangyougui/gf/v2/net/gsel" 18 "github.com/wangyougui/gf/v2/net/gsvc" 19 "github.com/wangyougui/gf/v2/text/gstr" 20 ) 21 22 type discoveryNode struct { 23 service gsvc.Service 24 address string 25 } 26 27 // Service is the client discovery service. 28 func (n *discoveryNode) Service() gsvc.Service { 29 return n.service 30 } 31 32 // Address returns the address of the node. 33 func (n *discoveryNode) Address() string { 34 return n.address 35 } 36 37 // service prefix to its selector map cache. 38 var clientSelectorMap = gmap.New(true) 39 40 // internalMiddlewareDiscovery is a client middleware that enables service discovery feature for client. 41 func internalMiddlewareDiscovery(c *Client, r *http.Request) (response *Response, err error) { 42 if c.discovery == nil && !isServiceName(r.URL.Host) { 43 return c.Next(r) 44 } 45 var ( 46 ctx = r.Context() 47 service gsvc.Service 48 ) 49 service, err = gsvc.GetAndWatchWithDiscovery(ctx, c.discovery, r.URL.Host, func(service gsvc.Service) { 50 intlog.Printf(ctx, `http client watching service "%s" changed`, service.GetPrefix()) 51 if v := clientSelectorMap.Get(service.GetPrefix()); v != nil { 52 if err = updateSelectorNodesByService(ctx, v.(gsel.Selector), service); err != nil { 53 intlog.Errorf(ctx, `%+v`, err) 54 } 55 } 56 }) 57 if err != nil { 58 if gerror.Code(err) == gcode.CodeNotFound { 59 intlog.Printf( 60 ctx, 61 `service discovery error with url "%s:%s":%s`, 62 r.Method, r.URL.String(), err.Error(), 63 ) 64 return c.Next(r) 65 } 66 } 67 if service == nil { 68 return c.Next(r) 69 } 70 // Balancer. 71 var ( 72 selectorMapKey = service.GetPrefix() 73 selectorMapValue = clientSelectorMap.GetOrSetFuncLock(selectorMapKey, func() interface{} { 74 intlog.Printf(ctx, `http client create selector for service "%s"`, selectorMapKey) 75 selector := c.builder.Build() 76 // Update selector nodes. 77 if err = updateSelectorNodesByService(ctx, selector, service); err != nil { 78 return nil 79 } 80 return selector 81 }) 82 ) 83 if err != nil { 84 return nil, err 85 } 86 selector := selectorMapValue.(gsel.Selector) 87 // Pick one node from multiple addresses. 88 node, done, err := selector.Pick(ctx) 89 if err != nil { 90 return nil, err 91 } 92 if done != nil { 93 defer done(ctx, gsel.DoneInfo{}) 94 } 95 r.Host = node.Address() 96 r.URL.Host = node.Address() 97 return c.Next(r) 98 } 99 100 func updateSelectorNodesByService(ctx context.Context, selector gsel.Selector, service gsvc.Service) error { 101 nodes := make(gsel.Nodes, 0) 102 for _, endpoint := range service.GetEndpoints() { 103 nodes = append(nodes, &discoveryNode{ 104 service: service, 105 address: endpoint.String(), 106 }) 107 } 108 return selector.Update(ctx, nodes) 109 } 110 111 // isServiceName checks and returns whether given input parameter is service name or not. 112 // It checks by whether the parameter is address by containing port delimiter character ':'. 113 // 114 // It does not contain any port number if using service discovery. 115 func isServiceName(serviceNameOrAddress string) bool { 116 return !gstr.Contains(serviceNameOrAddress, gsvc.EndpointHostPortDelimiter) 117 }