dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo3/dubbo3_protocol.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  	"fmt"
    23  	"reflect"
    24  	"sync"
    25  )
    26  
    27  import (
    28  	"github.com/dubbogo/gost/log/logger"
    29  
    30  	"github.com/dubbogo/grpc-go"
    31  	"github.com/dubbogo/grpc-go/metadata"
    32  
    33  	tripleConstant "github.com/dubbogo/triple/pkg/common/constant"
    34  	triConfig "github.com/dubbogo/triple/pkg/config"
    35  	"github.com/dubbogo/triple/pkg/triple"
    36  
    37  	"github.com/dustin/go-humanize"
    38  )
    39  
    40  import (
    41  	"dubbo.apache.org/dubbo-go/v3/common"
    42  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    43  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    44  	"dubbo.apache.org/dubbo-go/v3/config"
    45  	"dubbo.apache.org/dubbo-go/v3/protocol"
    46  	"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
    47  )
    48  
    49  var protocolOnce sync.Once
    50  
    51  func init() {
    52  	extension.SetProtocol(tripleConstant.TRIPLE, GetProtocol)
    53  	protocolOnce = sync.Once{}
    54  }
    55  
    56  var (
    57  	dubboProtocol *DubboProtocol
    58  )
    59  
    60  // DubboProtocol supports dubbo 3.0 protocol. It implements Protocol interface for dubbo protocol.
    61  type DubboProtocol struct {
    62  	protocol.BaseProtocol
    63  	serverLock sync.Mutex
    64  	serviceMap *sync.Map                       // serviceMap is used to export multiple service by one server
    65  	serverMap  map[string]*triple.TripleServer // serverMap stores all exported server
    66  }
    67  
    68  // NewDubboProtocol create a dubbo protocol.
    69  func NewDubboProtocol() *DubboProtocol {
    70  	return &DubboProtocol{
    71  		BaseProtocol: protocol.NewBaseProtocol(),
    72  		serverMap:    make(map[string]*triple.TripleServer),
    73  		serviceMap:   &sync.Map{},
    74  	}
    75  }
    76  
    77  // Export export dubbo3 service.
    78  func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
    79  	url := invoker.GetURL()
    80  	serviceKey := url.ServiceKey()
    81  	exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap(), dp.serviceMap)
    82  	dp.SetExporterMap(serviceKey, exporter)
    83  	logger.Infof("[Triple Protocol] Export service: %s", url.String())
    84  
    85  	key := url.GetParam(constant.BeanNameKey, "")
    86  	var service interface{}
    87  	service = config.GetProviderService(key)
    88  
    89  	serializationType := url.GetParam(constant.SerializationKey, constant.ProtobufSerialization)
    90  	var triSerializationType tripleConstant.CodecType
    91  
    92  	if serializationType == constant.ProtobufSerialization {
    93  		m, ok := reflect.TypeOf(service).MethodByName("XXX_SetProxyImpl")
    94  		if !ok {
    95  			logger.Errorf("PB service with key = %s is not support XXX_SetProxyImpl to pb."+
    96  				"Please run go install github.com/dubbogo/dubbogo-cli/cmd/protoc-gen-go-triple@latest to update your "+
    97  				"protoc-gen-go-triple and re-generate your pb file again.", key)
    98  			return nil
    99  		}
   100  		if invoker == nil {
   101  			panic(fmt.Sprintf("no invoker found for servicekey: %v", url.ServiceKey()))
   102  		}
   103  		in := []reflect.Value{reflect.ValueOf(service)}
   104  		in = append(in, reflect.ValueOf(invoker))
   105  		m.Func.Call(in)
   106  		triSerializationType = tripleConstant.PBCodecName
   107  	} else {
   108  		valueOf := reflect.ValueOf(service)
   109  		typeOf := valueOf.Type()
   110  		numField := valueOf.NumMethod()
   111  		tripleService := &UnaryService{proxyImpl: invoker}
   112  		for i := 0; i < numField; i++ {
   113  			ft := typeOf.Method(i)
   114  			if ft.Name == "Reference" {
   115  				continue
   116  			}
   117  			// get all method params type
   118  			typs := make([]reflect.Type, 0)
   119  			for j := 2; j < ft.Type.NumIn(); j++ {
   120  				typs = append(typs, ft.Type.In(j))
   121  			}
   122  			tripleService.setReqParamsTypes(ft.Name, typs)
   123  		}
   124  		service = tripleService
   125  		triSerializationType = tripleConstant.CodecType(serializationType)
   126  	}
   127  
   128  	dp.serviceMap.Store(url.GetParam(constant.InterfaceKey, ""), service)
   129  
   130  	// try start server
   131  	dp.openServer(url, triSerializationType)
   132  	return exporter
   133  }
   134  
   135  // Refer create dubbo3 service reference.
   136  func (dp *DubboProtocol) Refer(url *common.URL) protocol.Invoker {
   137  	invoker, err := NewDubboInvoker(url)
   138  	if err != nil {
   139  		logger.Errorf("Refer url = %+v, with error = %s", url, err.Error())
   140  		return nil
   141  	}
   142  	dp.SetInvokers(invoker)
   143  	logger.Infof("[Triple Protocol] Refer service: %s", url.String())
   144  	return invoker
   145  }
   146  
   147  // Destroy destroy dubbo3 service.
   148  func (dp *DubboProtocol) Destroy() {
   149  	dp.BaseProtocol.Destroy()
   150  	keyList := make([]string, 16)
   151  
   152  	dp.serverLock.Lock()
   153  	defer dp.serverLock.Unlock()
   154  	// Stop all server
   155  	for k, _ := range dp.serverMap {
   156  		keyList = append(keyList, k)
   157  	}
   158  	for _, v := range keyList {
   159  		if server := dp.serverMap[v]; server != nil {
   160  			server.Stop()
   161  		}
   162  		delete(dp.serverMap, v)
   163  	}
   164  }
   165  
   166  // Dubbo3GrpcService is gRPC service
   167  type Dubbo3GrpcService interface {
   168  	// SetProxyImpl sets proxy.
   169  	XXX_SetProxyImpl(impl protocol.Invoker)
   170  	// GetProxyImpl gets proxy.
   171  	XXX_GetProxyImpl() protocol.Invoker
   172  	// ServiceDesc gets an RPC service's specification.
   173  	XXX_ServiceDesc() *grpc.ServiceDesc
   174  }
   175  
   176  type UnaryService struct {
   177  	proxyImpl  protocol.Invoker
   178  	reqTypeMap sync.Map
   179  }
   180  
   181  func (d *UnaryService) setReqParamsTypes(methodName string, typ []reflect.Type) {
   182  	d.reqTypeMap.Store(methodName, typ)
   183  }
   184  
   185  func (d *UnaryService) GetReqParamsInterfaces(methodName string) ([]interface{}, bool) {
   186  	val, ok := d.reqTypeMap.Load(methodName)
   187  	if !ok {
   188  		return nil, false
   189  	}
   190  	typs := val.([]reflect.Type)
   191  	reqParamsInterfaces := make([]interface{}, 0, len(typs))
   192  	for _, typ := range typs {
   193  		reqParamsInterfaces = append(reqParamsInterfaces, reflect.New(typ).Interface())
   194  	}
   195  	return reqParamsInterfaces, true
   196  }
   197  
   198  func (d *UnaryService) InvokeWithArgs(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) {
   199  	dubboAttachment := make(map[string]interface{})
   200  	md, ok := metadata.FromIncomingContext(ctx)
   201  	if ok {
   202  		for k := range md {
   203  			dubboAttachment[k] = md.Get(k)[0]
   204  		}
   205  	}
   206  	res := d.proxyImpl.Invoke(ctx, invocation.NewRPCInvocation(methodName, arguments, dubboAttachment))
   207  	return res, res.Error()
   208  }
   209  
   210  // openServer open a dubbo3 server, if there is already a service using the same protocol, it returns directly.
   211  func (dp *DubboProtocol) openServer(url *common.URL, tripleCodecType tripleConstant.CodecType) {
   212  	dp.serverLock.Lock()
   213  	defer dp.serverLock.Unlock()
   214  	_, ok := dp.serverMap[url.Location]
   215  
   216  	if ok {
   217  		dp.serverMap[url.Location].RefreshService()
   218  		return
   219  	}
   220  
   221  	opts := []triConfig.OptionFunction{
   222  		triConfig.WithCodecType(tripleCodecType),
   223  		triConfig.WithLocation(url.Location),
   224  		triConfig.WithLogger(logger.GetLogger()),
   225  	}
   226  	tracingKey := url.GetParam(constant.TracingConfigKey, "")
   227  	if tracingKey != "" {
   228  		tracingConfig := config.GetTracingConfig(tracingKey)
   229  		if tracingConfig != nil {
   230  			if tracingConfig.ServiceName == "" {
   231  				tracingConfig.ServiceName = config.GetApplicationConfig().Name
   232  			}
   233  			switch tracingConfig.Name {
   234  			case "jaeger":
   235  				opts = append(opts, triConfig.WithJaegerConfig(
   236  					tracingConfig.Address,
   237  					tracingConfig.ServiceName,
   238  					*tracingConfig.UseAgent,
   239  				))
   240  			default:
   241  				logger.Warnf("unsupported tracing name %s, now triple only support jaeger", tracingConfig.Name)
   242  			}
   243  		}
   244  	}
   245  
   246  	maxServerRecvMsgSize := constant.DefaultMaxServerRecvMsgSize
   247  	if recvMsgSize, err := humanize.ParseBytes(url.GetParam(constant.MaxServerRecvMsgSize, "")); err == nil && recvMsgSize != 0 {
   248  		maxServerRecvMsgSize = int(recvMsgSize)
   249  	}
   250  	maxServerSendMsgSize := constant.DefaultMaxServerSendMsgSize
   251  	if sendMsgSize, err := humanize.ParseBytes(url.GetParam(constant.MaxServerSendMsgSize, "")); err == nil && sendMsgSize != 0 {
   252  		maxServerSendMsgSize = int(sendMsgSize)
   253  	}
   254  	opts = append(opts, triConfig.WithGRPCMaxServerRecvMessageSize(maxServerRecvMsgSize))
   255  	opts = append(opts, triConfig.WithGRPCMaxServerSendMessageSize(maxServerSendMsgSize))
   256  
   257  	triOption := triConfig.NewTripleOption(opts...)
   258  
   259  	tlsConfig := config.GetRootConfig().TLSConfig
   260  	if tlsConfig != nil {
   261  		triOption.TLSCertFile = tlsConfig.TLSCertFile
   262  		triOption.TLSKeyFile = tlsConfig.TLSKeyFile
   263  		triOption.CACertFile = tlsConfig.CACertFile
   264  		triOption.TLSServerName = tlsConfig.TLSServerName
   265  		logger.Infof("Triple Server initialized the TLSConfig configuration")
   266  	}
   267  
   268  	_, ok = dp.ExporterMap().Load(url.ServiceKey())
   269  	if !ok {
   270  		panic("[DubboProtocol]" + url.Key() + "is not existing")
   271  	}
   272  
   273  	srv := triple.NewTripleServer(dp.serviceMap, triOption)
   274  	dp.serverMap[url.Location] = srv
   275  	srv.Start()
   276  }
   277  
   278  // GetProtocol get a single dubbo3 protocol.
   279  func GetProtocol() protocol.Protocol {
   280  	protocolOnce.Do(func() {
   281  		dubboProtocol = NewDubboProtocol()
   282  	})
   283  	return dubboProtocol
   284  }