dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo3/dubbo3_invoker.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  package dubbo3
    19  
    20  import (
    21  	"context"
    22  	"reflect"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  )
    28  
    29  import (
    30  	"github.com/dubbogo/gost/log/logger"
    31  
    32  	"github.com/dubbogo/grpc-go/metadata"
    33  
    34  	tripleConstant "github.com/dubbogo/triple/pkg/common/constant"
    35  	triConfig "github.com/dubbogo/triple/pkg/config"
    36  	"github.com/dubbogo/triple/pkg/triple"
    37  
    38  	"github.com/dustin/go-humanize"
    39  )
    40  
    41  import (
    42  	"dubbo.apache.org/dubbo-go/v3/common"
    43  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    44  	"dubbo.apache.org/dubbo-go/v3/config"
    45  	"dubbo.apache.org/dubbo-go/v3/protocol"
    46  	invocation_impl "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
    47  )
    48  
    49  // same as dubbo_invoker.go attachmentKey
    50  var attachmentKey = []string{
    51  	constant.InterfaceKey, constant.GroupKey, constant.TokenKey, constant.TimeoutKey,
    52  	constant.VersionKey, tripleConstant.TripleServiceGroup, tripleConstant.TripleServiceVersion,
    53  }
    54  
    55  // DubboInvoker is implement of protocol.Invoker, a dubboInvoker refer to one service and ip.
    56  type DubboInvoker struct {
    57  	protocol.BaseInvoker
    58  	// the net layer client, it is focus on network communication.
    59  	client *triple.TripleClient
    60  	// quitOnce is used to make sure DubboInvoker is only destroyed once
    61  	quitOnce sync.Once
    62  	// timeout for service(interface) level.
    63  	timeout time.Duration
    64  	// clientGuard is the client lock of dubbo invoker
    65  	clientGuard *sync.RWMutex
    66  }
    67  
    68  // NewDubboInvoker constructor
    69  func NewDubboInvoker(url *common.URL) (*DubboInvoker, error) {
    70  	rt := config.GetConsumerConfig().RequestTimeout
    71  
    72  	timeout := url.GetParamDuration(constant.TimeoutKey, rt)
    73  	// for triple pb serialization. The bean name from provider is the provider reference key,
    74  	// which can't locate the target consumer stub, so we use interface key..
    75  	interfaceKey := url.GetParam(constant.InterfaceKey, "")
    76  	consumerService := config.GetConsumerServiceByInterfaceName(interfaceKey)
    77  
    78  	dubboSerializerType := url.GetParam(constant.SerializationKey, constant.ProtobufSerialization)
    79  	triCodecType := tripleConstant.CodecType(dubboSerializerType)
    80  	// new triple client
    81  	opts := []triConfig.OptionFunction{
    82  		triConfig.WithClientTimeout(timeout),
    83  		triConfig.WithCodecType(triCodecType),
    84  		triConfig.WithLocation(url.Location),
    85  		triConfig.WithHeaderAppVersion(url.GetParam(constant.AppVersionKey, "")),
    86  		triConfig.WithHeaderGroup(url.GetParam(constant.GroupKey, "")),
    87  		triConfig.WithLogger(logger.GetLogger()),
    88  	}
    89  	maxCallRecvMsgSize := constant.DefaultMaxCallRecvMsgSize
    90  	if maxCall, err := humanize.ParseBytes(url.GetParam(constant.MaxCallRecvMsgSize, "")); err == nil && maxCall != 0 {
    91  		maxCallRecvMsgSize = int(maxCall)
    92  	}
    93  	maxCallSendMsgSize := constant.DefaultMaxCallSendMsgSize
    94  	if maxCall, err := humanize.ParseBytes(url.GetParam(constant.MaxCallSendMsgSize, "")); err == nil && maxCall != 0 {
    95  		maxCallSendMsgSize = int(maxCall)
    96  	}
    97  	opts = append(opts, triConfig.WithGRPCMaxCallRecvMessageSize(maxCallRecvMsgSize))
    98  	opts = append(opts, triConfig.WithGRPCMaxCallSendMessageSize(maxCallSendMsgSize))
    99  
   100  	tracingKey := url.GetParam(constant.TracingConfigKey, "")
   101  	if tracingKey != "" {
   102  		tracingConfig := config.GetTracingConfig(tracingKey)
   103  		if tracingConfig != nil {
   104  			if tracingConfig.Name == "jaeger" {
   105  				if tracingConfig.ServiceName == "" {
   106  					tracingConfig.ServiceName = config.GetApplicationConfig().Name
   107  				}
   108  				opts = append(opts, triConfig.WithJaegerConfig(
   109  					tracingConfig.Address,
   110  					tracingConfig.ServiceName,
   111  					*tracingConfig.UseAgent,
   112  				))
   113  			} else {
   114  				logger.Warnf("unsupported tracing name %s, now triple only support jaeger", tracingConfig.Name)
   115  			}
   116  		}
   117  	}
   118  
   119  	triOption := triConfig.NewTripleOption(opts...)
   120  	tlsConfig := config.GetRootConfig().TLSConfig
   121  	if tlsConfig != nil {
   122  		triOption.TLSCertFile = tlsConfig.TLSCertFile
   123  		triOption.TLSKeyFile = tlsConfig.TLSKeyFile
   124  		triOption.CACertFile = tlsConfig.CACertFile
   125  		triOption.TLSServerName = tlsConfig.TLSServerName
   126  		logger.Infof("Triple Client initialized the TLSConfig configuration")
   127  	}
   128  	client, err := triple.NewTripleClient(consumerService, triOption)
   129  
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	return &DubboInvoker{
   135  		BaseInvoker: *protocol.NewBaseInvoker(url),
   136  		client:      client,
   137  		timeout:     timeout,
   138  		clientGuard: &sync.RWMutex{},
   139  	}, nil
   140  }
   141  
   142  func (di *DubboInvoker) setClient(client *triple.TripleClient) {
   143  	di.clientGuard.Lock()
   144  	defer di.clientGuard.Unlock()
   145  
   146  	di.client = client
   147  }
   148  
   149  func (di *DubboInvoker) getClient() *triple.TripleClient {
   150  	di.clientGuard.RLock()
   151  	defer di.clientGuard.RUnlock()
   152  
   153  	return di.client
   154  }
   155  
   156  // Invoke call remoting.
   157  func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
   158  	var (
   159  		result protocol.RPCResult
   160  	)
   161  
   162  	if !di.BaseInvoker.IsAvailable() {
   163  		// Generally, the case will not happen, because the invoker has been removed
   164  		// from the invoker list before destroy,so no new request will enter the destroyed invoker
   165  		logger.Warnf("this dubboInvoker is destroyed")
   166  		result.Err = protocol.ErrDestroyedInvoker
   167  		return &result
   168  	}
   169  
   170  	di.clientGuard.RLock()
   171  	defer di.clientGuard.RUnlock()
   172  
   173  	if di.client == nil {
   174  		result.Err = protocol.ErrClientClosed
   175  		return &result
   176  	}
   177  
   178  	if !di.BaseInvoker.IsAvailable() {
   179  		// Generally, the case will not happen, because the invoker has been removed
   180  		// from the invoker list before destroy,so no new request will enter the destroyed invoker
   181  		logger.Warnf("this grpcInvoker is destroying")
   182  		result.Err = protocol.ErrDestroyedInvoker
   183  		return &result
   184  	}
   185  
   186  	for _, k := range attachmentKey {
   187  		var paramKey string
   188  		switch k {
   189  		case tripleConstant.TripleServiceGroup:
   190  			paramKey = constant.GroupKey
   191  		case tripleConstant.TripleServiceVersion:
   192  			paramKey = constant.VersionKey
   193  		default:
   194  			paramKey = k
   195  		}
   196  
   197  		if v := di.GetURL().GetParam(paramKey, ""); len(v) > 0 {
   198  			invocation.SetAttachment(k, v)
   199  		}
   200  	}
   201  
   202  	// append interface id to ctx
   203  	gRPCMD := make(metadata.MD, 0)
   204  	// triple will convert attachment value to []string
   205  	for k, v := range invocation.Attachments() {
   206  		if str, ok := v.(string); ok {
   207  			gRPCMD.Set(k, str)
   208  			continue
   209  		}
   210  		if str, ok := v.([]string); ok {
   211  			gRPCMD.Set(k, str...)
   212  			continue
   213  		}
   214  		logger.Warnf("[Triple Protocol]Triple attachment value with key = %s is invalid, which should be string or []string", k)
   215  	}
   216  	ctx = metadata.NewOutgoingContext(ctx, gRPCMD)
   217  	ctx = context.WithValue(ctx, tripleConstant.InterfaceKey, di.BaseInvoker.GetURL().GetParam(constant.InterfaceKey, ""))
   218  	in := make([]reflect.Value, 0, 16)
   219  	in = append(in, reflect.ValueOf(ctx))
   220  
   221  	if len(invocation.ParameterValues()) > 0 {
   222  		in = append(in, invocation.ParameterValues()...)
   223  	}
   224  
   225  	methodName := invocation.MethodName()
   226  	triAttachmentWithErr := di.client.Invoke(methodName, in, invocation.Reply())
   227  	result.Err = triAttachmentWithErr.GetError()
   228  	result.Attrs = make(map[string]interface{})
   229  	for k, v := range triAttachmentWithErr.GetAttachments() {
   230  		result.Attrs[k] = v
   231  	}
   232  	result.Rest = invocation.Reply()
   233  	return &result
   234  }
   235  
   236  // get timeout including methodConfig
   237  func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) time.Duration {
   238  	timeout := di.GetURL().GetParam(strings.Join([]string{constant.MethodKeys, invocation.MethodName(), constant.TimeoutKey}, "."), "")
   239  	if len(timeout) != 0 {
   240  		if t, err := time.ParseDuration(timeout); err == nil {
   241  			// config timeout into attachment
   242  			invocation.SetAttachment(constant.TimeoutKey, strconv.Itoa(int(t.Milliseconds())))
   243  			return t
   244  		}
   245  	}
   246  	// set timeout into invocation at method level
   247  	invocation.SetAttachment(constant.TimeoutKey, strconv.Itoa(int(di.timeout.Milliseconds())))
   248  	return di.timeout
   249  }
   250  
   251  // IsAvailable check if invoker is available, now it is useless
   252  func (di *DubboInvoker) IsAvailable() bool {
   253  	client := di.getClient()
   254  	if client != nil {
   255  		// FIXME here can't check if tcp server is started now!!!
   256  		return client.IsAvailable()
   257  	}
   258  	return false
   259  }
   260  
   261  // Destroy destroy dubbo3 client invoker.
   262  func (di *DubboInvoker) Destroy() {
   263  	di.quitOnce.Do(func() {
   264  		di.BaseInvoker.Destroy()
   265  		client := di.getClient()
   266  		if client != nil {
   267  			di.setClient(nil)
   268  			client.Close()
   269  		}
   270  	})
   271  }