google.golang.org/grpc@v1.72.2/resolver_wrapper.go (about)

     1  /*
     2   *
     3   * Copyright 2017 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 grpc
    20  
    21  import (
    22  	"context"
    23  	"strings"
    24  	"sync"
    25  
    26  	"google.golang.org/grpc/internal/channelz"
    27  	"google.golang.org/grpc/internal/grpcsync"
    28  	"google.golang.org/grpc/internal/pretty"
    29  	"google.golang.org/grpc/internal/resolver/delegatingresolver"
    30  	"google.golang.org/grpc/resolver"
    31  	"google.golang.org/grpc/serviceconfig"
    32  )
    33  
    34  // ccResolverWrapper is a wrapper on top of cc for resolvers.
    35  // It implements resolver.ClientConn interface.
    36  type ccResolverWrapper struct {
    37  	// The following fields are initialized when the wrapper is created and are
    38  	// read-only afterwards, and therefore can be accessed without a mutex.
    39  	cc                  *ClientConn
    40  	ignoreServiceConfig bool
    41  	serializer          *grpcsync.CallbackSerializer
    42  	serializerCancel    context.CancelFunc
    43  
    44  	resolver resolver.Resolver // only accessed within the serializer
    45  
    46  	// The following fields are protected by mu.  Caller must take cc.mu before
    47  	// taking mu.
    48  	mu       sync.Mutex
    49  	curState resolver.State
    50  	closed   bool
    51  }
    52  
    53  // newCCResolverWrapper initializes the ccResolverWrapper.  It can only be used
    54  // after calling start, which builds the resolver.
    55  func newCCResolverWrapper(cc *ClientConn) *ccResolverWrapper {
    56  	ctx, cancel := context.WithCancel(cc.ctx)
    57  	return &ccResolverWrapper{
    58  		cc:                  cc,
    59  		ignoreServiceConfig: cc.dopts.disableServiceConfig,
    60  		serializer:          grpcsync.NewCallbackSerializer(ctx),
    61  		serializerCancel:    cancel,
    62  	}
    63  }
    64  
    65  // start builds the name resolver using the resolver.Builder in cc and returns
    66  // any error encountered.  It must always be the first operation performed on
    67  // any newly created ccResolverWrapper, except that close may be called instead.
    68  func (ccr *ccResolverWrapper) start() error {
    69  	errCh := make(chan error)
    70  	ccr.serializer.TrySchedule(func(ctx context.Context) {
    71  		if ctx.Err() != nil {
    72  			return
    73  		}
    74  		opts := resolver.BuildOptions{
    75  			DisableServiceConfig: ccr.cc.dopts.disableServiceConfig,
    76  			DialCreds:            ccr.cc.dopts.copts.TransportCredentials,
    77  			CredsBundle:          ccr.cc.dopts.copts.CredsBundle,
    78  			Dialer:               ccr.cc.dopts.copts.Dialer,
    79  			Authority:            ccr.cc.authority,
    80  			MetricsRecorder:      ccr.cc.metricsRecorderList,
    81  		}
    82  		var err error
    83  		// The delegating resolver is used unless:
    84  		//   - A custom dialer is provided via WithContextDialer dialoption or
    85  		//   - Proxy usage is disabled through WithNoProxy dialoption.
    86  		// In these cases, the resolver is built based on the scheme of target,
    87  		// using the appropriate resolver builder.
    88  		if ccr.cc.dopts.copts.Dialer != nil || !ccr.cc.dopts.useProxy {
    89  			ccr.resolver, err = ccr.cc.resolverBuilder.Build(ccr.cc.parsedTarget, ccr, opts)
    90  		} else {
    91  			ccr.resolver, err = delegatingresolver.New(ccr.cc.parsedTarget, ccr, opts, ccr.cc.resolverBuilder, ccr.cc.dopts.enableLocalDNSResolution)
    92  		}
    93  		errCh <- err
    94  	})
    95  	return <-errCh
    96  }
    97  
    98  func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
    99  	ccr.serializer.TrySchedule(func(ctx context.Context) {
   100  		if ctx.Err() != nil || ccr.resolver == nil {
   101  			return
   102  		}
   103  		ccr.resolver.ResolveNow(o)
   104  	})
   105  }
   106  
   107  // close initiates async shutdown of the wrapper.  To determine the wrapper has
   108  // finished shutting down, the channel should block on ccr.serializer.Done()
   109  // without cc.mu held.
   110  func (ccr *ccResolverWrapper) close() {
   111  	channelz.Info(logger, ccr.cc.channelz, "Closing the name resolver")
   112  	ccr.mu.Lock()
   113  	ccr.closed = true
   114  	ccr.mu.Unlock()
   115  
   116  	ccr.serializer.TrySchedule(func(context.Context) {
   117  		if ccr.resolver == nil {
   118  			return
   119  		}
   120  		ccr.resolver.Close()
   121  		ccr.resolver = nil
   122  	})
   123  	ccr.serializerCancel()
   124  }
   125  
   126  // UpdateState is called by resolver implementations to report new state to gRPC
   127  // which includes addresses and service config.
   128  func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
   129  	ccr.cc.mu.Lock()
   130  	ccr.mu.Lock()
   131  	if ccr.closed {
   132  		ccr.mu.Unlock()
   133  		ccr.cc.mu.Unlock()
   134  		return nil
   135  	}
   136  	if s.Endpoints == nil {
   137  		s.Endpoints = addressesToEndpoints(s.Addresses)
   138  	}
   139  	ccr.addChannelzTraceEvent(s)
   140  	ccr.curState = s
   141  	ccr.mu.Unlock()
   142  	return ccr.cc.updateResolverStateAndUnlock(s, nil)
   143  }
   144  
   145  // ReportError is called by resolver implementations to report errors
   146  // encountered during name resolution to gRPC.
   147  func (ccr *ccResolverWrapper) ReportError(err error) {
   148  	ccr.cc.mu.Lock()
   149  	ccr.mu.Lock()
   150  	if ccr.closed {
   151  		ccr.mu.Unlock()
   152  		ccr.cc.mu.Unlock()
   153  		return
   154  	}
   155  	ccr.mu.Unlock()
   156  	channelz.Warningf(logger, ccr.cc.channelz, "ccResolverWrapper: reporting error to cc: %v", err)
   157  	ccr.cc.updateResolverStateAndUnlock(resolver.State{}, err)
   158  }
   159  
   160  // NewAddress is called by the resolver implementation to send addresses to
   161  // gRPC.
   162  func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
   163  	ccr.cc.mu.Lock()
   164  	ccr.mu.Lock()
   165  	if ccr.closed {
   166  		ccr.mu.Unlock()
   167  		ccr.cc.mu.Unlock()
   168  		return
   169  	}
   170  	s := resolver.State{
   171  		Addresses:     addrs,
   172  		ServiceConfig: ccr.curState.ServiceConfig,
   173  		Endpoints:     addressesToEndpoints(addrs),
   174  	}
   175  	ccr.addChannelzTraceEvent(s)
   176  	ccr.curState = s
   177  	ccr.mu.Unlock()
   178  	ccr.cc.updateResolverStateAndUnlock(s, nil)
   179  }
   180  
   181  // ParseServiceConfig is called by resolver implementations to parse a JSON
   182  // representation of the service config.
   183  func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
   184  	return parseServiceConfig(scJSON, ccr.cc.dopts.maxCallAttempts)
   185  }
   186  
   187  // addChannelzTraceEvent adds a channelz trace event containing the new
   188  // state received from resolver implementations.
   189  func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
   190  	if !logger.V(0) && !channelz.IsOn() {
   191  		return
   192  	}
   193  	var updates []string
   194  	var oldSC, newSC *ServiceConfig
   195  	var oldOK, newOK bool
   196  	if ccr.curState.ServiceConfig != nil {
   197  		oldSC, oldOK = ccr.curState.ServiceConfig.Config.(*ServiceConfig)
   198  	}
   199  	if s.ServiceConfig != nil {
   200  		newSC, newOK = s.ServiceConfig.Config.(*ServiceConfig)
   201  	}
   202  	if oldOK != newOK || (oldOK && newOK && oldSC.rawJSONString != newSC.rawJSONString) {
   203  		updates = append(updates, "service config updated")
   204  	}
   205  	if len(ccr.curState.Addresses) > 0 && len(s.Addresses) == 0 {
   206  		updates = append(updates, "resolver returned an empty address list")
   207  	} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
   208  		updates = append(updates, "resolver returned new addresses")
   209  	}
   210  	channelz.Infof(logger, ccr.cc.channelz, "Resolver state updated: %s (%v)", pretty.ToJSON(s), strings.Join(updates, "; "))
   211  }
   212  
   213  func addressesToEndpoints(addrs []resolver.Address) []resolver.Endpoint {
   214  	endpoints := make([]resolver.Endpoint, 0, len(addrs))
   215  	for _, a := range addrs {
   216  		ep := resolver.Endpoint{Addresses: []resolver.Address{a}, Attributes: a.BalancerAttributes}
   217  		ep.Addresses[0].BalancerAttributes = nil
   218  		endpoints = append(endpoints, ep)
   219  	}
   220  	return endpoints
   221  }