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 }