dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/balancer/clusterresolver/resource_resolver.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * 20 * Copyright 2021 gRPC authors. 21 * 22 */ 23 24 package clusterresolver 25 26 import ( 27 "sync" 28 ) 29 30 import ( 31 "dubbo.apache.org/dubbo-go/v3/xds/client" 32 "dubbo.apache.org/dubbo-go/v3/xds/client/resource" 33 ) 34 35 // resourceUpdate is a combined update from all the resources, in the order of 36 // priority. For example, it can be {EDS, EDS, DNS}. 37 type resourceUpdate struct { 38 priorities []priorityConfig 39 err error 40 } 41 42 type discoveryMechanism interface { 43 lastUpdate() (interface{}, bool) 44 resolveNow() 45 stop() 46 } 47 48 // discoveryMechanismKey is {type+resource_name}, it's used as the map key, so 49 // that the same resource resolver can be reused (e.g. when there are two 50 // mechanisms, both for the same EDS resource, but has different circuit 51 // breaking config. 52 type discoveryMechanismKey struct { 53 typ DiscoveryMechanismType 54 name string 55 } 56 57 // resolverMechanismTuple is needed to keep the resolver and the discovery 58 // mechanism together, because resolvers can be shared. And we need the 59 // mechanism for fields like circuit breaking, LRS etc when generating the 60 // balancer config. 61 type resolverMechanismTuple struct { 62 dm DiscoveryMechanism 63 dmKey discoveryMechanismKey 64 r discoveryMechanism 65 } 66 67 type resourceResolver struct { 68 parent *clusterResolverBalancer 69 updateChannel chan *resourceUpdate 70 71 // mu protects the slice and map, and content of the resolvers in the slice. 72 mu sync.Mutex 73 mechanisms []DiscoveryMechanism 74 children []resolverMechanismTuple 75 childrenMap map[discoveryMechanismKey]discoveryMechanism 76 } 77 78 func newResourceResolver(parent *clusterResolverBalancer) *resourceResolver { 79 return &resourceResolver{ 80 parent: parent, 81 updateChannel: make(chan *resourceUpdate, 1), 82 childrenMap: make(map[discoveryMechanismKey]discoveryMechanism), 83 } 84 } 85 86 func equalDiscoveryMechanisms(a, b []DiscoveryMechanism) bool { 87 if len(a) != len(b) { 88 return false 89 } 90 for i, aa := range a { 91 bb := b[i] 92 if !aa.Equal(bb) { 93 return false 94 } 95 } 96 return true 97 } 98 99 func (rr *resourceResolver) updateMechanisms(mechanisms []DiscoveryMechanism) { 100 rr.mu.Lock() 101 defer rr.mu.Unlock() 102 if equalDiscoveryMechanisms(rr.mechanisms, mechanisms) { 103 return 104 } 105 rr.mechanisms = mechanisms 106 rr.children = make([]resolverMechanismTuple, len(mechanisms)) 107 newDMs := make(map[discoveryMechanismKey]bool) 108 109 // Start one watch for each new discover mechanism {type+resource_name}. 110 for i, dm := range mechanisms { 111 switch dm.Type { 112 case DiscoveryMechanismTypeEDS: 113 // If EDSServiceName is not set, use the cluster name as EDS service 114 // name to watch. 115 nameToWatch := dm.EDSServiceName 116 if nameToWatch == "" { 117 nameToWatch = dm.Cluster 118 } 119 dmKey := discoveryMechanismKey{typ: dm.Type, name: nameToWatch} 120 newDMs[dmKey] = true 121 122 r := rr.childrenMap[dmKey] 123 if r == nil { 124 r = newEDSResolver(nameToWatch, rr.parent.xdsClient, rr) 125 rr.childrenMap[dmKey] = r 126 } 127 rr.children[i] = resolverMechanismTuple{dm: dm, dmKey: dmKey, r: r} 128 case DiscoveryMechanismTypeLogicalDNS: 129 // Name to resolve in DNS is the hostname, not the ClientConn 130 // target. 131 dmKey := discoveryMechanismKey{typ: dm.Type, name: dm.DNSHostname} 132 newDMs[dmKey] = true 133 134 r := rr.childrenMap[dmKey] 135 if r == nil { 136 r = newDNSResolver(dm.DNSHostname, rr) 137 rr.childrenMap[dmKey] = r 138 } 139 rr.children[i] = resolverMechanismTuple{dm: dm, dmKey: dmKey, r: r} 140 } 141 } 142 // Stop the resources that were removed. 143 for dm, r := range rr.childrenMap { 144 if !newDMs[dm] { 145 delete(rr.childrenMap, dm) 146 r.stop() 147 } 148 } 149 // Regenerate even if there's no change in discovery mechanism, in case 150 // priority order changed. 151 rr.generate() 152 } 153 154 // resolveNow is typically called to trigger re-resolve of DNS. The EDS 155 // resolveNow() is a noop. 156 func (rr *resourceResolver) resolveNow() { 157 rr.mu.Lock() 158 defer rr.mu.Unlock() 159 for _, r := range rr.childrenMap { 160 r.resolveNow() 161 } 162 } 163 164 func (rr *resourceResolver) stop() { 165 rr.mu.Lock() 166 defer rr.mu.Unlock() 167 for dm, r := range rr.childrenMap { 168 delete(rr.childrenMap, dm) 169 r.stop() 170 } 171 rr.mechanisms = nil 172 rr.children = nil 173 } 174 175 // generate collects all the updates from all the resolvers, and push the 176 // combined result into the update channel. It only pushes the update when all 177 // the child resolvers have received at least one update, otherwise it will 178 // wait. 179 // 180 // caller must hold rr.mu. 181 func (rr *resourceResolver) generate() { 182 var ret []priorityConfig 183 for _, rDM := range rr.children { 184 r, ok := rr.childrenMap[rDM.dmKey] 185 if !ok { 186 rr.parent.logger.Infof("resolver for %+v not found, should never happen", rDM.dmKey) 187 continue 188 } 189 190 u, ok := r.lastUpdate() 191 if !ok { 192 // Don't send updates to parent until all resolvers have update to 193 // send. 194 return 195 } 196 switch uu := u.(type) { 197 case resource.EndpointsUpdate: 198 ret = append(ret, priorityConfig{mechanism: rDM.dm, edsResp: uu}) 199 case []string: 200 ret = append(ret, priorityConfig{mechanism: rDM.dm, addresses: uu}) 201 } 202 } 203 select { 204 case <-rr.updateChannel: 205 default: 206 } 207 rr.updateChannel <- &resourceUpdate{priorities: ret} 208 } 209 210 type edsDiscoveryMechanism struct { 211 cancel func() 212 213 update resource.EndpointsUpdate 214 updateReceived bool 215 } 216 217 func (er *edsDiscoveryMechanism) lastUpdate() (interface{}, bool) { 218 if !er.updateReceived { 219 return nil, false 220 } 221 return er.update, true 222 } 223 224 func (er *edsDiscoveryMechanism) resolveNow() { 225 } 226 227 func (er *edsDiscoveryMechanism) stop() { 228 er.cancel() 229 } 230 231 // newEDSResolver starts the EDS watch on the given xds client. 232 func newEDSResolver(nameToWatch string, xdsc client.XDSClient, topLevelResolver *resourceResolver) *edsDiscoveryMechanism { 233 ret := &edsDiscoveryMechanism{} 234 topLevelResolver.parent.logger.Infof("EDS watch started on %v", nameToWatch) 235 cancel := xdsc.WatchEndpoints(nameToWatch, func(update resource.EndpointsUpdate, err error) { 236 topLevelResolver.mu.Lock() 237 defer topLevelResolver.mu.Unlock() 238 if err != nil { 239 select { 240 case <-topLevelResolver.updateChannel: 241 default: 242 } 243 topLevelResolver.updateChannel <- &resourceUpdate{err: err} 244 return 245 } 246 ret.update = update 247 ret.updateReceived = true 248 topLevelResolver.generate() 249 }) 250 ret.cancel = func() { 251 topLevelResolver.parent.logger.Infof("EDS watch canceled on %v", nameToWatch) 252 cancel() 253 } 254 return ret 255 }