dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/serviceconfig/serviceconfig.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 serviceconfig contains utility functions to parse service config.
    25  package serviceconfig
    26  
    27  import (
    28  	"encoding/json"
    29  	"fmt"
    30  	"time"
    31  )
    32  
    33  import (
    34  	"github.com/dubbogo/gost/log/logger"
    35  
    36  	"google.golang.org/grpc/balancer"
    37  
    38  	"google.golang.org/grpc/codes"
    39  
    40  	externalserviceconfig "google.golang.org/grpc/serviceconfig"
    41  )
    42  
    43  // BalancerConfig wraps the name and config associated with one load balancing
    44  // policy. It corresponds to a single entry of the loadBalancingConfig field
    45  // from ServiceConfig.
    46  //
    47  // It implements the json.Unmarshaler interface.
    48  //
    49  // https://github.com/grpc/grpc-proto/blob/54713b1e8bc6ed2d4f25fb4dff527842150b91b2/grpc/service_config/service_config.proto#L247
    50  type BalancerConfig struct {
    51  	Name   string
    52  	Config externalserviceconfig.LoadBalancingConfig
    53  }
    54  
    55  type intermediateBalancerConfig []map[string]json.RawMessage
    56  
    57  // MarshalJSON implements the json.Marshaler interface.
    58  //
    59  // It marshals the balancer and config into a length-1 slice
    60  // ([]map[string]config).
    61  func (bc *BalancerConfig) MarshalJSON() ([]byte, error) {
    62  	if bc.Config == nil {
    63  		// If config is nil, return empty config `{}`.
    64  		return []byte(fmt.Sprintf(`[{%q: %v}]`, bc.Name, "{}")), nil
    65  	}
    66  	c, err := json.Marshal(bc.Config)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	return []byte(fmt.Sprintf(`[{%q: %s}]`, bc.Name, c)), nil
    71  }
    72  
    73  // UnmarshalJSON implements the json.Unmarshaler interface.
    74  //
    75  // ServiceConfig contains a list of loadBalancingConfigs, each with a name and
    76  // config. This method iterates through that list in order, and stops at the
    77  // first policy that is supported.
    78  //   - If the config for the first supported policy is invalid, the whole service
    79  //     config is invalid.
    80  //   - If the list doesn't contain any supported policy, the whole service config
    81  //     is invalid.
    82  func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
    83  	var ir intermediateBalancerConfig
    84  	err := json.Unmarshal(b, &ir)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	var names []string
    90  	for i, lbcfg := range ir {
    91  		if len(lbcfg) != 1 {
    92  			return fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg)
    93  		}
    94  
    95  		var (
    96  			name    string
    97  			jsonCfg json.RawMessage
    98  		)
    99  		// Get the key:value pair from the map. We have already made sure that
   100  		// the map contains a single entry.
   101  		for name, jsonCfg = range lbcfg {
   102  		}
   103  
   104  		names = append(names, name)
   105  		builder := balancer.Get(name)
   106  		if builder == nil {
   107  			// If the balancer is not registered, move on to the next config.
   108  			// This is not an error.
   109  			continue
   110  		}
   111  		bc.Name = name
   112  
   113  		parser, ok := builder.(balancer.ConfigParser)
   114  		if !ok {
   115  			if string(jsonCfg) != "{}" {
   116  				logger.Warnf("non-empty balancer configuration %q, but balancer does not implement ParseConfig", string(jsonCfg))
   117  			}
   118  			// Stop at this, though the builder doesn't support parsing config.
   119  			return nil
   120  		}
   121  
   122  		cfg, err := parser.ParseConfig(jsonCfg)
   123  		if err != nil {
   124  			return fmt.Errorf("error parsing loadBalancingConfig for policy %q: %v", name, err)
   125  		}
   126  		bc.Config = cfg
   127  		return nil
   128  	}
   129  	// This is reached when the for loop iterates over all entries, but didn't
   130  	// return. This means we had a loadBalancingConfig slice but did not
   131  	// encounter a registered policy. The config is considered invalid in this
   132  	// case.
   133  	return fmt.Errorf("invalid loadBalancingConfig: no supported policies found in %v", names)
   134  }
   135  
   136  // MethodConfig defines the configuration recommended by the service providers for a
   137  // particular method.
   138  type MethodConfig struct {
   139  	// WaitForReady indicates whether RPCs sent to this method should wait until
   140  	// the connection is ready by default (!failfast). The value specified via the
   141  	// gRPC client API will override the value set here.
   142  	WaitForReady *bool
   143  	// Timeout is the default timeout for RPCs sent to this method. The actual
   144  	// deadline used will be the minimum of the value specified here and the value
   145  	// set by the application via the gRPC client API.  If either one is not set,
   146  	// then the other will be used.  If neither is set, then the RPC has no deadline.
   147  	Timeout *time.Duration
   148  	// MaxReqSize is the maximum allowed payload size for an individual request in a
   149  	// stream (client->server) in bytes. The size which is measured is the serialized
   150  	// payload after per-message compression (but before stream compression) in bytes.
   151  	// The actual value used is the minimum of the value specified here and the value set
   152  	// by the application via the gRPC client API. If either one is not set, then the other
   153  	// will be used.  If neither is set, then the built-in default is used.
   154  	MaxReqSize *int
   155  	// MaxRespSize is the maximum allowed payload size for an individual response in a
   156  	// stream (server->client) in bytes.
   157  	MaxRespSize *int
   158  	// RetryPolicy configures retry options for the method.
   159  	RetryPolicy *RetryPolicy
   160  }
   161  
   162  // RetryPolicy defines the go-native version of the retry policy defined by the
   163  // service config here:
   164  // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#integration-with-service-config
   165  type RetryPolicy struct {
   166  	// MaxAttempts is the maximum number of attempts, including the original RPC.
   167  	//
   168  	// This field is required and must be two or greater.
   169  	MaxAttempts int
   170  
   171  	// Exponential backoff parameters. The initial retry attempt will occur at
   172  	// random(0, initialBackoff). In general, the nth attempt will occur at
   173  	// random(0,
   174  	//   min(initialBackoff*backoffMultiplier**(n-1), maxBackoff)).
   175  	//
   176  	// These fields are required and must be greater than zero.
   177  	InitialBackoff    time.Duration
   178  	MaxBackoff        time.Duration
   179  	BackoffMultiplier float64
   180  
   181  	// The set of status codes which may be retried.
   182  	//
   183  	// Status codes are specified as strings, e.g., "UNAVAILABLE".
   184  	//
   185  	// This field is required and must be non-empty.
   186  	// Note: a set is used to store this for easy lookup.
   187  	RetryableStatusCodes map[codes.Code]bool
   188  }