github.com/cloudwego/kitex@v0.9.0/pkg/discovery/discovery.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package discovery defines interfaces for service discovery.
    18  // Developers that are willing to customize service discovery
    19  // should implement their own Resolver and supply it with the
    20  // option WithResolver at client's creation.
    21  package discovery
    22  
    23  import (
    24  	"context"
    25  	"net"
    26  
    27  	"github.com/cloudwego/kitex/pkg/rpcinfo"
    28  	"github.com/cloudwego/kitex/pkg/utils"
    29  )
    30  
    31  // DefaultWeight is the default weight for an instance.
    32  const DefaultWeight = 10
    33  
    34  // Result contains the result of service discovery process.
    35  // Cacheable tells whether the instance list can/should be cached.
    36  // When Cacheable is true, CacheKey can be used to map the instance list in cache.
    37  type Result struct {
    38  	Cacheable bool
    39  	CacheKey  string
    40  	Instances []Instance
    41  }
    42  
    43  // Change contains the difference between the current discovery result and the previous one.
    44  // It is designed for providing detail information when dispatching an event for service
    45  // discovery result change.
    46  // Since the loadbalancer may rely on caching the result of resolver to improve performance,
    47  // the resolver implementation should dispatch an event when result changes.
    48  type Change struct {
    49  	Result  Result
    50  	Added   []Instance
    51  	Updated []Instance
    52  	Removed []Instance
    53  }
    54  
    55  // Resolver resolves the target endpoint into a list of Instance.
    56  type Resolver interface {
    57  	// Target should return a description for the given target that is suitable for being a key for cache.
    58  	Target(ctx context.Context, target rpcinfo.EndpointInfo) (description string)
    59  
    60  	// Resolve returns a list of instances for the given description of a target.
    61  	Resolve(ctx context.Context, desc string) (Result, error)
    62  
    63  	// Diff computes the difference between two results.
    64  	// When `next` is cacheable, the Change should be cacheable, too. And the `Result` field's CacheKey in
    65  	// the return value should be set with the given cacheKey.
    66  	Diff(cacheKey string, prev, next Result) (Change, bool)
    67  
    68  	// Name returns the name of the resolver.
    69  	Name() string
    70  }
    71  
    72  // DefaultDiff provides a natural implementation for the Diff method of the Resolver interface.
    73  func DefaultDiff(cacheKey string, prev, next Result) (Change, bool) {
    74  	ch := Change{
    75  		Result: Result{
    76  			Cacheable: next.Cacheable,
    77  			CacheKey:  cacheKey,
    78  			Instances: next.Instances,
    79  		},
    80  	}
    81  
    82  	prevMap := make(map[string]struct{}, len(prev.Instances))
    83  	for _, ins := range prev.Instances {
    84  		prevMap[ins.Address().String()] = struct{}{}
    85  	}
    86  
    87  	nextMap := make(map[string]struct{}, len(next.Instances))
    88  	for _, ins := range next.Instances {
    89  		addr := ins.Address().String()
    90  		nextMap[addr] = struct{}{}
    91  		if _, found := prevMap[addr]; !found {
    92  			ch.Added = append(ch.Added, ins)
    93  		}
    94  	}
    95  
    96  	for _, ins := range prev.Instances {
    97  		if _, found := nextMap[ins.Address().String()]; !found {
    98  			ch.Removed = append(ch.Removed, ins)
    99  		}
   100  	}
   101  	return ch, len(ch.Added)+len(ch.Removed) != 0
   102  }
   103  
   104  type instance struct {
   105  	addr   net.Addr
   106  	weight int
   107  	tags   map[string]string
   108  }
   109  
   110  func (i *instance) Address() net.Addr {
   111  	return i.addr
   112  }
   113  
   114  func (i *instance) Weight() int {
   115  	return i.weight
   116  }
   117  
   118  func (i *instance) Tag(key string) (value string, exist bool) {
   119  	value, exist = i.tags[key]
   120  	return
   121  }
   122  
   123  // NewInstance creates a Instance using the given network, address and tags
   124  func NewInstance(network, address string, weight int, tags map[string]string) Instance {
   125  	return &instance{
   126  		addr:   utils.NewNetAddr(network, address),
   127  		weight: weight,
   128  		tags:   tags,
   129  	}
   130  }
   131  
   132  // SynthesizedResolver synthesizes a Resolver using a resolve function.
   133  type SynthesizedResolver struct {
   134  	TargetFunc  func(ctx context.Context, target rpcinfo.EndpointInfo) string
   135  	ResolveFunc func(ctx context.Context, key string) (Result, error)
   136  	DiffFunc    func(key string, prev, next Result) (Change, bool)
   137  	NameFunc    func() string
   138  }
   139  
   140  // Target implements the Resolver interface.
   141  func (sr SynthesizedResolver) Target(ctx context.Context, target rpcinfo.EndpointInfo) string {
   142  	if sr.TargetFunc == nil {
   143  		return ""
   144  	}
   145  	return sr.TargetFunc(ctx, target)
   146  }
   147  
   148  // Resolve implements the Resolver interface.
   149  func (sr SynthesizedResolver) Resolve(ctx context.Context, key string) (Result, error) {
   150  	return sr.ResolveFunc(ctx, key)
   151  }
   152  
   153  // Diff implements the Resolver interface.
   154  func (sr SynthesizedResolver) Diff(key string, prev, next Result) (Change, bool) {
   155  	if sr.DiffFunc == nil {
   156  		return DefaultDiff(key, prev, next)
   157  	}
   158  	return sr.DiffFunc(key, prev, next)
   159  }
   160  
   161  // Name implements the Resolver interface
   162  func (sr SynthesizedResolver) Name() string {
   163  	if sr.NameFunc == nil {
   164  		return ""
   165  	}
   166  	return sr.NameFunc()
   167  }
   168  
   169  // Instance contains information of an instance from the target service.
   170  type Instance interface {
   171  	Address() net.Addr
   172  	Weight() int
   173  	Tag(key string) (value string, exist bool)
   174  }