gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/xds/internal/resolver/watch_service.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * 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 package resolver 20 21 import ( 22 "fmt" 23 "sync" 24 "time" 25 26 "gitee.com/ks-custle/core-gm/grpc/internal/grpclog" 27 "gitee.com/ks-custle/core-gm/grpc/internal/pretty" 28 "gitee.com/ks-custle/core-gm/grpc/xds/internal/clusterspecifier" 29 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient" 30 "gitee.com/ks-custle/core-gm/grpc/xds/internal/xdsclient/xdsresource" 31 ) 32 33 // serviceUpdate contains information received from the LDS/RDS responses which 34 // are of interest to the xds resolver. The RDS request is built by first 35 // making a LDS to get the RouteConfig name. 36 type serviceUpdate struct { 37 // virtualHost contains routes and other configuration to route RPCs. 38 virtualHost *xdsresource.VirtualHost 39 // clusterSpecifierPlugins contains the configurations for any cluster 40 // specifier plugins emitted by the xdsclient. 41 clusterSpecifierPlugins map[string]clusterspecifier.BalancerConfig 42 // ldsConfig contains configuration that applies to all routes. 43 ldsConfig ldsConfig 44 } 45 46 // ldsConfig contains information received from the LDS responses which are of 47 // interest to the xds resolver. 48 type ldsConfig struct { 49 // maxStreamDuration is from the HTTP connection manager's 50 // common_http_protocol_options field. 51 maxStreamDuration time.Duration 52 httpFilterConfig []xdsresource.HTTPFilter 53 } 54 55 // watchService uses LDS and RDS to discover information about the provided 56 // serviceName. 57 // 58 // Note that during race (e.g. an xDS response is received while the user is 59 // calling cancel()), there's a small window where the callback can be called 60 // after the watcher is canceled. The caller needs to handle this case. 61 func watchService(c xdsclient.XDSClient, serviceName string, cb func(serviceUpdate, error), logger *grpclog.PrefixLogger) (cancel func()) { 62 w := &serviceUpdateWatcher{ 63 logger: logger, 64 c: c, 65 serviceName: serviceName, 66 serviceCb: cb, 67 } 68 w.ldsCancel = c.WatchListener(serviceName, w.handleLDSResp) 69 70 return w.close 71 } 72 73 // serviceUpdateWatcher handles LDS and RDS response, and calls the service 74 // callback at the right time. 75 type serviceUpdateWatcher struct { 76 logger *grpclog.PrefixLogger 77 c xdsclient.XDSClient 78 serviceName string 79 ldsCancel func() 80 serviceCb func(serviceUpdate, error) 81 lastUpdate serviceUpdate 82 83 mu sync.Mutex 84 closed bool 85 rdsName string 86 rdsCancel func() 87 } 88 89 func (w *serviceUpdateWatcher) handleLDSResp(update xdsresource.ListenerUpdate, err error) { 90 w.logger.Infof("received LDS update: %+v, err: %v", pretty.ToJSON(update), err) 91 w.mu.Lock() 92 defer w.mu.Unlock() 93 if w.closed { 94 return 95 } 96 if err != nil { 97 // We check the error type and do different things. For now, the only 98 // type we check is ResourceNotFound, which indicates the LDS resource 99 // was removed, and besides sending the error to callback, we also 100 // cancel the RDS watch. 101 if xdsresource.ErrType(err) == xdsresource.ErrorTypeResourceNotFound && w.rdsCancel != nil { 102 w.rdsCancel() 103 w.rdsName = "" 104 w.rdsCancel = nil 105 w.lastUpdate = serviceUpdate{} 106 } 107 // The other error cases still return early without canceling the 108 // existing RDS watch. 109 w.serviceCb(serviceUpdate{}, err) 110 return 111 } 112 113 w.lastUpdate.ldsConfig = ldsConfig{ 114 maxStreamDuration: update.MaxStreamDuration, 115 httpFilterConfig: update.HTTPFilters, 116 } 117 118 if update.InlineRouteConfig != nil { 119 // If there was an RDS watch, cancel it. 120 w.rdsName = "" 121 if w.rdsCancel != nil { 122 w.rdsCancel() 123 w.rdsCancel = nil 124 } 125 126 // Handle the inline RDS update as if it's from an RDS watch. 127 w.applyRouteConfigUpdate(*update.InlineRouteConfig) 128 return 129 } 130 131 // RDS name from update is not an empty string, need RDS to fetch the 132 // routes. 133 134 if w.rdsName == update.RouteConfigName { 135 // If the new RouteConfigName is same as the previous, don't cancel and 136 // restart the RDS watch. 137 // 138 // If the route name did change, then we must wait until the first RDS 139 // update before reporting this LDS config. 140 if w.lastUpdate.virtualHost != nil { 141 // We want to send an update with the new fields from the new LDS 142 // (e.g. max stream duration), and old fields from the the previous 143 // RDS. 144 // 145 // But note that this should only happen when virtual host is set, 146 // which means an RDS was received. 147 w.serviceCb(w.lastUpdate, nil) 148 } 149 return 150 } 151 w.rdsName = update.RouteConfigName 152 if w.rdsCancel != nil { 153 w.rdsCancel() 154 } 155 w.rdsCancel = w.c.WatchRouteConfig(update.RouteConfigName, w.handleRDSResp) 156 } 157 158 func (w *serviceUpdateWatcher) applyRouteConfigUpdate(update xdsresource.RouteConfigUpdate) { 159 matchVh := xdsresource.FindBestMatchingVirtualHost(w.serviceName, update.VirtualHosts) 160 if matchVh == nil { 161 // No matching virtual host found. 162 w.serviceCb(serviceUpdate{}, fmt.Errorf("no matching virtual host found for %q", w.serviceName)) 163 return 164 } 165 166 w.lastUpdate.virtualHost = matchVh 167 w.lastUpdate.clusterSpecifierPlugins = update.ClusterSpecifierPlugins 168 w.serviceCb(w.lastUpdate, nil) 169 } 170 171 func (w *serviceUpdateWatcher) handleRDSResp(update xdsresource.RouteConfigUpdate, err error) { 172 w.logger.Infof("received RDS update: %+v, err: %v", pretty.ToJSON(update), err) 173 w.mu.Lock() 174 defer w.mu.Unlock() 175 if w.closed { 176 return 177 } 178 if w.rdsCancel == nil { 179 // This mean only the RDS watch is canceled, can happen if the LDS 180 // resource is removed. 181 return 182 } 183 if err != nil { 184 w.serviceCb(serviceUpdate{}, err) 185 return 186 } 187 w.applyRouteConfigUpdate(update) 188 } 189 190 func (w *serviceUpdateWatcher) close() { 191 w.mu.Lock() 192 defer w.mu.Unlock() 193 w.closed = true 194 w.ldsCancel() 195 if w.rdsCancel != nil { 196 w.rdsCancel() 197 w.rdsCancel = nil 198 } 199 }