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  }