dubbo.apache.org/dubbo-go/v3@v3.1.1/common/rpc_service.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 common
    19  
    20  import (
    21  	"context"
    22  	"reflect"
    23  	"strings"
    24  	"sync"
    25  	"unicode"
    26  	"unicode/utf8"
    27  )
    28  
    29  import (
    30  	"github.com/dubbogo/gost/log/logger"
    31  
    32  	perrors "github.com/pkg/errors"
    33  )
    34  
    35  // RPCService the type alias of interface{}
    36  type RPCService = interface{}
    37  
    38  // ReferencedRPCService is the rpc service interface which wraps base Reference method.
    39  //
    40  // Reference method refers rpc service id or reference id.
    41  type ReferencedRPCService interface {
    42  	Reference() string
    43  }
    44  
    45  // TriplePBService is  the type alias of interface{}
    46  type TriplePBService interface {
    47  	XXX_InterfaceName() string
    48  }
    49  
    50  // GetReference return the reference id of the service.
    51  // If the service implemented the ReferencedRPCService interface,
    52  // it will call the Reference method. If not, it will
    53  // return the struct name as the reference id.
    54  func GetReference(service RPCService) string {
    55  	if s, ok := service.(ReferencedRPCService); ok {
    56  		return s.Reference()
    57  	}
    58  
    59  	ref := ""
    60  	sType := reflect.TypeOf(service)
    61  	kind := sType.Kind()
    62  	switch kind {
    63  	case reflect.Struct:
    64  		ref = sType.Name()
    65  	case reflect.Ptr:
    66  		sName := sType.Elem().Name()
    67  		if sName != "" {
    68  			ref = sName
    69  		} else {
    70  			ref = sType.Elem().Field(0).Name
    71  		}
    72  	}
    73  	return ref
    74  }
    75  
    76  // AsyncCallbackService callback interface for async
    77  type AsyncCallbackService interface {
    78  	CallBack(response CallbackResponse)
    79  }
    80  
    81  // CallbackResponse for different protocol
    82  type CallbackResponse interface{}
    83  
    84  // AsyncCallback async callback method
    85  type AsyncCallback func(response CallbackResponse)
    86  
    87  const (
    88  	METHOD_MAPPER = "MethodMapper"
    89  )
    90  
    91  var (
    92  	// Precompute the reflect type for error. Can't use error directly
    93  	// because Typeof takes an empty interface value. This is annoying.
    94  	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
    95  
    96  	// ServiceMap store description of service.
    97  	ServiceMap = &serviceMap{
    98  		serviceMap:   make(map[string]map[string]*Service),
    99  		interfaceMap: make(map[string][]*Service),
   100  	}
   101  )
   102  
   103  // MethodType is description of service method.
   104  type MethodType struct {
   105  	method    reflect.Method
   106  	ctxType   reflect.Type   // request context
   107  	argsType  []reflect.Type // args except ctx, include replyType if existing
   108  	replyType reflect.Type   // return value, otherwise it is nil
   109  }
   110  
   111  // Method gets @m.method.
   112  func (m *MethodType) Method() reflect.Method {
   113  	return m.method
   114  }
   115  
   116  // CtxType gets @m.ctxType.
   117  func (m *MethodType) CtxType() reflect.Type {
   118  	return m.ctxType
   119  }
   120  
   121  // ArgsType gets @m.argsType.
   122  func (m *MethodType) ArgsType() []reflect.Type {
   123  	return m.argsType
   124  }
   125  
   126  // ReplyType gets @m.replyType.
   127  func (m *MethodType) ReplyType() reflect.Type {
   128  	return m.replyType
   129  }
   130  
   131  // SuiteContext transfers @ctx to reflect.Value type or get it from @m.ctxType.
   132  func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value {
   133  	if ctxV := reflect.ValueOf(ctx); ctxV.IsValid() {
   134  		return ctxV
   135  	}
   136  	return reflect.Zero(m.ctxType)
   137  }
   138  
   139  // Service is description of service
   140  type Service struct {
   141  	name     string
   142  	rcvr     reflect.Value
   143  	rcvrType reflect.Type
   144  	methods  map[string]*MethodType
   145  }
   146  
   147  // Method gets @s.methods.
   148  func (s *Service) Method() map[string]*MethodType {
   149  	return s.methods
   150  }
   151  
   152  // Name will return service name
   153  func (s *Service) Name() string {
   154  	return s.name
   155  }
   156  
   157  // RcvrType gets @s.rcvrType.
   158  func (s *Service) RcvrType() reflect.Type {
   159  	return s.rcvrType
   160  }
   161  
   162  // Rcvr gets @s.rcvr.
   163  func (s *Service) Rcvr() reflect.Value {
   164  	return s.rcvr
   165  }
   166  
   167  type serviceMap struct {
   168  	mutex        sync.RWMutex                   // protects the serviceMap
   169  	serviceMap   map[string]map[string]*Service // protocol -> service name -> service
   170  	interfaceMap map[string][]*Service          // interface -> service
   171  }
   172  
   173  // GetService gets a service definition by protocol and name
   174  func (sm *serviceMap) GetService(protocol, interfaceName, group, version string) *Service {
   175  	serviceKey := ServiceKey(interfaceName, group, version)
   176  	return sm.GetServiceByServiceKey(protocol, serviceKey)
   177  }
   178  
   179  // GetServiceByServiceKey gets a service definition by protocol and service key
   180  func (sm *serviceMap) GetServiceByServiceKey(protocol, serviceKey string) *Service {
   181  	sm.mutex.RLock()
   182  	defer sm.mutex.RUnlock()
   183  	if s, ok := sm.serviceMap[protocol]; ok {
   184  		if srv, ok := s[serviceKey]; ok {
   185  			return srv
   186  		}
   187  		return nil
   188  	}
   189  	return nil
   190  }
   191  
   192  // GetInterface gets an interface definition by interface name
   193  func (sm *serviceMap) GetInterface(interfaceName string) []*Service {
   194  	sm.mutex.RLock()
   195  	defer sm.mutex.RUnlock()
   196  	if s, ok := sm.interfaceMap[interfaceName]; ok {
   197  		return s
   198  	}
   199  	return nil
   200  }
   201  
   202  // Register registers a service by @interfaceName and @protocol
   203  func (sm *serviceMap) Register(interfaceName, protocol, group, version string, rcvr RPCService) (string, error) {
   204  	if sm.serviceMap[protocol] == nil {
   205  		sm.serviceMap[protocol] = make(map[string]*Service)
   206  	}
   207  	if sm.interfaceMap[interfaceName] == nil {
   208  		sm.interfaceMap[interfaceName] = make([]*Service, 0, 16)
   209  	}
   210  
   211  	s := new(Service)
   212  	s.rcvrType = reflect.TypeOf(rcvr)
   213  	s.rcvr = reflect.ValueOf(rcvr)
   214  	sname := reflect.Indirect(s.rcvr).Type().Name()
   215  	if sname == "" {
   216  		s := "no service name for type " + s.rcvrType.String()
   217  		logger.Errorf(s)
   218  		return "", perrors.New(s)
   219  	}
   220  	if !isExported(sname) {
   221  		s := "type " + sname + " is not exported"
   222  		logger.Errorf(s)
   223  		return "", perrors.New(s)
   224  	}
   225  
   226  	sname = ServiceKey(interfaceName, group, version)
   227  	if server := sm.GetService(protocol, interfaceName, group, version); server != nil {
   228  		return "", perrors.New("service already defined: " + sname)
   229  	}
   230  	s.name = sname
   231  	s.methods = make(map[string]*MethodType)
   232  
   233  	// Install the methods
   234  	methods := ""
   235  	methods, s.methods = suitableMethods(s.rcvrType)
   236  
   237  	if len(s.methods) == 0 {
   238  		s := "type " + sname + " has no exported methods of suitable type"
   239  		logger.Errorf(s)
   240  		return "", perrors.New(s)
   241  	}
   242  	sm.mutex.Lock()
   243  	sm.serviceMap[protocol][s.name] = s
   244  	sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], s)
   245  	sm.mutex.Unlock()
   246  
   247  	return strings.TrimSuffix(methods, ","), nil
   248  }
   249  
   250  // UnRegister cancels a service by @interfaceName, @protocol and @serviceId
   251  func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceKey string) error {
   252  	if protocol == "" || serviceKey == "" {
   253  		return perrors.New("protocol or ServiceKey is nil")
   254  	}
   255  
   256  	var (
   257  		err   error
   258  		index = -1
   259  		svcs  map[string]*Service
   260  		svrs  []*Service
   261  		ok    bool
   262  	)
   263  
   264  	f := func() error {
   265  		sm.mutex.RLock()
   266  		defer sm.mutex.RUnlock()
   267  		svcs, ok = sm.serviceMap[protocol]
   268  		if !ok {
   269  			return perrors.New("no services for " + protocol)
   270  		}
   271  		s, ok := svcs[serviceKey]
   272  		if !ok {
   273  			return perrors.New("no service for " + serviceKey)
   274  		}
   275  		svrs, ok = sm.interfaceMap[interfaceName]
   276  		if !ok {
   277  			return perrors.New("no service for " + interfaceName)
   278  		}
   279  		for i, svr := range svrs {
   280  			if svr == s {
   281  				index = i
   282  			}
   283  		}
   284  		return nil
   285  	}
   286  
   287  	if err = f(); err != nil {
   288  		return err
   289  	}
   290  
   291  	sm.mutex.Lock()
   292  	defer sm.mutex.Unlock()
   293  	sm.interfaceMap[interfaceName] = make([]*Service, 0, len(svrs))
   294  	for i := range svrs {
   295  		if i != index {
   296  			sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i])
   297  		}
   298  	}
   299  	delete(svcs, serviceKey)
   300  	if len(sm.serviceMap[protocol]) == 0 {
   301  		delete(sm.serviceMap, protocol)
   302  	}
   303  
   304  	return nil
   305  }
   306  
   307  // Is this an exported - upper case - name
   308  func isExported(name string) bool {
   309  	s, _ := utf8.DecodeRuneInString(name)
   310  	return unicode.IsUpper(s)
   311  }
   312  
   313  // Is this type exported or a builtin?
   314  func isExportedOrBuiltinType(t reflect.Type) bool {
   315  	for t.Kind() == reflect.Ptr {
   316  		t = t.Elem()
   317  	}
   318  	// PkgPath will be non-empty even for an exported type,
   319  	// so we need to check the type name as well.
   320  	return isExported(t.Name()) || t.PkgPath() == ""
   321  }
   322  
   323  // suitableMethods returns suitable Rpc methods of typ
   324  func suitableMethods(typ reflect.Type) (string, map[string]*MethodType) {
   325  	methods := make(map[string]*MethodType)
   326  	var mts []string
   327  	logger.Debugf("[%s] NumMethod is %d", typ.String(), typ.NumMethod())
   328  	method, ok := typ.MethodByName(METHOD_MAPPER)
   329  	var methodMapper map[string]string
   330  	if ok && method.Type.NumIn() == 1 && method.Type.NumOut() == 1 && method.Type.Out(0).String() == "map[string]string" {
   331  		methodMapper = method.Func.Call([]reflect.Value{reflect.New(typ.Elem())})[0].Interface().(map[string]string)
   332  	}
   333  
   334  	for m := 0; m < typ.NumMethod(); m++ {
   335  		method = typ.Method(m)
   336  		if mt := suiteMethod(method); mt != nil {
   337  			methodName, ok := methodMapper[method.Name]
   338  			if !ok {
   339  				methodName = method.Name
   340  			}
   341  			methods[methodName] = mt
   342  			mts = append(mts, methodName)
   343  		}
   344  	}
   345  	return strings.Join(mts, ","), methods
   346  }
   347  
   348  // suiteMethod returns a suitable Rpc methodType
   349  func suiteMethod(method reflect.Method) *MethodType {
   350  	mtype := method.Type
   351  	mname := method.Name
   352  	inNum := mtype.NumIn()
   353  	outNum := mtype.NumOut()
   354  
   355  	// Method must be exported.
   356  	if method.PkgPath != "" {
   357  		return nil
   358  	}
   359  
   360  	var (
   361  		replyType, ctxType reflect.Type
   362  		argsType           []reflect.Type
   363  	)
   364  
   365  	// Reference is used to define service reference, and method with prefix 'XXX' is generated by triple pb tool.
   366  	// SetGRPCServer is used for pb reflection.
   367  	// They should not to be checked.
   368  	if mname == "Reference" || mname == "SetGRPCServer" || strings.HasPrefix(mname, "XXX") {
   369  		return nil
   370  	}
   371  
   372  	if outNum != 1 && outNum != 2 {
   373  		logger.Warnf("method %s of mtype %v has wrong number of in out parameters %d; needs exactly 1/2",
   374  			mname, mtype.String(), outNum)
   375  		return nil
   376  	}
   377  
   378  	// The latest return type of the method must be error.
   379  	if returnType := mtype.Out(outNum - 1); returnType != typeOfError {
   380  		logger.Debugf(`"%s" method will not be exported because its last return type %v doesn't have error`, mname, returnType)
   381  		return nil
   382  	}
   383  
   384  	// replyType
   385  	if outNum == 2 {
   386  		replyType = mtype.Out(0)
   387  		if !isExportedOrBuiltinType(replyType) {
   388  			logger.Errorf("reply type of method %s not exported{%v}", mname, replyType)
   389  			return nil
   390  		}
   391  	}
   392  
   393  	index := 1
   394  
   395  	// ctxType
   396  	if inNum > 1 && mtype.In(1).String() == "context.Context" {
   397  		ctxType = mtype.In(1)
   398  		index = 2
   399  	}
   400  
   401  	for ; index < inNum; index++ {
   402  		argsType = append(argsType, mtype.In(index))
   403  		// need not be a pointer.
   404  		if !isExportedOrBuiltinType(mtype.In(index)) {
   405  			logger.Errorf("argument type of method %q is not exported %v", mname, mtype.In(index))
   406  			return nil
   407  		}
   408  	}
   409  
   410  	return &MethodType{method: method, argsType: argsType, replyType: replyType, ctxType: ctxType}
   411  }