github.com/kaydxh/golang@v0.0.131/pkg/resolver/resolver.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package resolver 23 24 import ( 25 "context" 26 "fmt" 27 "math/rand" 28 "sync" 29 "time" 30 31 "github.com/kaydxh/golang/go/errors" 32 errors_ "github.com/kaydxh/golang/go/errors" 33 time_ "github.com/kaydxh/golang/go/time" 34 dns_ "github.com/kaydxh/golang/pkg/resolver/dns" 35 k8sdns_ "github.com/kaydxh/golang/pkg/resolver/dns/k8s-resolver" 36 netdns_ "github.com/kaydxh/golang/pkg/resolver/dns/net-resolver" 37 "github.com/serialx/hashring" 38 "github.com/sirupsen/logrus" 39 "go.uber.org/atomic" 40 ) 41 42 type ResolverQueryMap sync.Map 43 44 type ResolverOptions struct { 45 resolverType Resolver_ResolverType 46 loadBalanceMode Resolver_LoadBalanceMode 47 //k8s 48 nodeGroup string 49 nodeUnit string 50 } 51 52 type ResolverQuery struct { 53 // domain: domain for net resolver, Ex: cube-svc.ns.svc 54 // domain: svc name for k8s resolver, Ex: cube 55 Domain string 56 nodes []string 57 hashring *hashring.HashRing 58 opts ResolverOptions 59 resolver dns_.DNSResolver 60 } 61 62 //NewDefaultResolverQuery, dns reslover, bls consist hash 63 func NewDefaultResolverQuery(domain string) ResolverQuery { 64 rq := ResolverQuery{ 65 Domain: domain, 66 } 67 rq.SetDefault() 68 return rq 69 } 70 71 func defaultResolverOptions() ResolverOptions { 72 return ResolverOptions{ 73 resolverType: Resolver_resolver_type_dns, 74 loadBalanceMode: Resolver_load_balance_mode_consist, 75 } 76 } 77 78 func (r *ResolverQuery) SetDefault() { 79 r.opts = defaultResolverOptions() 80 } 81 82 func NewResolverQuery(domain string, opts ...ResolverQueryOption) (ResolverQuery, error) { 83 rq := NewDefaultResolverQuery(domain) 84 rq.ApplyOptions(opts...) 85 86 // if resolver is not set, use default 87 if rq.resolver == nil { 88 err := rq.SetResolver() 89 if err != nil { 90 return rq, err 91 } 92 } 93 94 return rq, nil 95 } 96 97 func (r *ResolverQuery) SetResolver() error { 98 var err error 99 switch r.opts.resolverType { 100 case Resolver_resolver_type_k8s: 101 r.resolver, err = k8sdns_.NewK8sDNSResolver( 102 k8sdns_.WithNodeGroup(r.opts.nodeGroup), 103 k8sdns_.WithNodeUnit(r.opts.nodeUnit), 104 ) 105 if err != nil { 106 logrus.Errorf("new k8s resolver, err: %v", err) 107 return err 108 } 109 default: 110 r.resolver = netdns_.DefaultResolver 111 } 112 113 return nil 114 } 115 116 type ResolverService struct { 117 resolverInterval time.Duration 118 serviceByName ResolverQueryMap 119 inShutdown atomic.Bool 120 mu sync.Mutex 121 cancel func() 122 } 123 124 func NewDefaultResolverServices(resolverInterval time.Duration, domains ...string) *ResolverService { 125 rs := NewResolverService(resolverInterval) 126 for _, domain := range domains { 127 rq := NewDefaultResolverQuery(domain) 128 rs.AddService(rq) 129 } 130 return rs 131 } 132 133 func NewResolverService(resolverInterval time.Duration, services ...ResolverQuery) *ResolverService { 134 rs := &ResolverService{ 135 resolverInterval: resolverInterval, 136 } 137 if rs.resolverInterval == 0 { 138 rs.resolverInterval = 10 * time.Second 139 } 140 141 rs.AddServices(services...) 142 return rs 143 } 144 145 func (srv *ResolverService) logger() logrus.FieldLogger { 146 return logrus.WithField("module", "ResolverService") 147 } 148 149 func (srv *ResolverService) Run(ctx context.Context) error { 150 logger := srv.logger() 151 logger.Infoln("ResolverService Run") 152 if srv.inShutdown.Load() { 153 logger.Infoln("ResolverService Shutdown") 154 return fmt.Errorf("server closed") 155 } 156 go func() { 157 errors.HandleError(srv.Serve(ctx)) 158 }() 159 return nil 160 161 } 162 163 func (srv *ResolverService) Serve(ctx context.Context) error { 164 logger := srv.logger() 165 logger.Infoln("ServiceResolver Serve") 166 167 if srv.inShutdown.Load() { 168 err := fmt.Errorf("server closed") 169 logger.WithError(err).Errorf("ServiceResolver Serve canceled") 170 return err 171 } 172 173 defer srv.inShutdown.Store(true) 174 ctx, cancel := context.WithCancel(ctx) 175 srv.mu.Lock() 176 srv.cancel = cancel 177 srv.mu.Unlock() 178 179 time_.UntilWithContxt(ctx, func(ctx context.Context) error { 180 logger.Debugf("querying services") 181 err := srv.QueryServices() 182 if err != nil { 183 logger.WithError(err).Errorf("query services failed") 184 return err 185 } 186 logger.Debugf("query services") 187 return nil 188 }, srv.resolverInterval) 189 logger.Info("stopped query services") 190 return nil 191 } 192 193 func (srv *ResolverService) Shutdown() { 194 srv.inShutdown.Store(true) 195 srv.mu.Lock() 196 defer srv.mu.Unlock() 197 if srv.cancel != nil { 198 srv.cancel() 199 } 200 } 201 202 func (srv *ResolverService) QueryServices() (err error) { 203 var ( 204 errs []error 205 ) 206 207 logger := srv.logger() 208 srv.serviceByName.Range(func(name string, service ResolverQuery) bool { 209 210 service.nodes, err = service.resolver.LookupHostIPv4(context.Background(), service.Domain) 211 if err != nil { 212 errs = append(errs, fmt.Errorf("failed to query service: %v, err: %v", service.Domain, err)) 213 return true 214 } 215 logger.Debugf("the results of lookup domain[%s] are nodes[%v]", service.Domain, service.nodes) 216 217 if service.opts.loadBalanceMode == Resolver_load_balance_mode_consist { 218 service.hashring = hashring.New(service.nodes) 219 } 220 srv.serviceByName.Store(name, service) 221 return true 222 223 }) 224 225 return errors_.NewAggregate(errs) 226 } 227 228 func (srv *ResolverService) PickNode(name string, consistKey string) (node string, has bool) { 229 service, has := srv.serviceByName.Load(name) 230 if !has { 231 return "", false 232 } 233 234 if service.opts.loadBalanceMode == Resolver_load_balance_mode_consist { 235 if service.hashring == nil { 236 err := srv.QueryServices() 237 if err != nil { 238 return "", false 239 } 240 service, has = srv.serviceByName.Load(name) 241 if !has { 242 return "", false 243 } 244 } 245 return service.hashring.GetNode(consistKey) 246 } 247 248 if len(service.nodes) == 0 { 249 return "", false 250 } 251 s := service.nodes[rand.Intn(len(service.nodes))] 252 return s, true 253 } 254 255 // AddService, if exist, it will update 256 func (srv *ResolverService) AddService(service ResolverQuery) { 257 srv.serviceByName.Store(service.Domain, service) 258 } 259 260 // AddServices, if exist, it will update 261 func (srv *ResolverService) AddServices(services ...ResolverQuery) { 262 for _, service := range services { 263 srv.AddService(service) 264 } 265 }