dubbo.apache.org/dubbo-go/v3@v3.1.1/proxy/proxy.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 proxy
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"reflect"
    24  	"sync"
    25  )
    26  
    27  import (
    28  	"github.com/apache/dubbo-go-hessian2/java_exception"
    29  
    30  	"github.com/dubbogo/gost/log/logger"
    31  
    32  	perrors "github.com/pkg/errors"
    33  )
    34  
    35  import (
    36  	"dubbo.apache.org/dubbo-go/v3/common"
    37  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    38  	"dubbo.apache.org/dubbo-go/v3/protocol"
    39  	invocation_impl "dubbo.apache.org/dubbo-go/v3/protocol/invocation"
    40  )
    41  
    42  // nolint
    43  type Proxy struct {
    44  	rpc         common.RPCService
    45  	invoke      protocol.Invoker
    46  	callback    interface{}
    47  	attachments map[string]string
    48  	implement   ImplementFunc
    49  	once        sync.Once
    50  }
    51  
    52  type (
    53  	// ProxyOption a function to init Proxy with options
    54  	ProxyOption func(p *Proxy)
    55  	// ImplementFunc function for proxy impl of RPCService functions
    56  	ImplementFunc func(p *Proxy, v common.RPCService)
    57  )
    58  
    59  var typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
    60  
    61  // NewProxy create service proxy.
    62  func NewProxy(invoke protocol.Invoker, callback interface{}, attachments map[string]string) *Proxy {
    63  	return NewProxyWithOptions(invoke, callback, attachments,
    64  		WithProxyImplementFunc(DefaultProxyImplementFunc))
    65  }
    66  
    67  // NewProxyWithOptions create service proxy with options.
    68  func NewProxyWithOptions(invoke protocol.Invoker, callback interface{}, attachments map[string]string, opts ...ProxyOption) *Proxy {
    69  	p := &Proxy{
    70  		invoke:      invoke,
    71  		callback:    callback,
    72  		attachments: attachments,
    73  	}
    74  	for _, opt := range opts {
    75  		opt(p)
    76  	}
    77  	return p
    78  }
    79  
    80  // WithProxyImplementFunc an option function to setup proxy.ImplementFunc
    81  func WithProxyImplementFunc(f ImplementFunc) ProxyOption {
    82  	return func(p *Proxy) {
    83  		p.implement = f
    84  	}
    85  }
    86  
    87  // Implement
    88  // proxy implement
    89  // In consumer, RPCService like:
    90  //
    91  //			type XxxProvider struct {
    92  //	 		Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error
    93  //			}
    94  func (p *Proxy) Implement(v common.RPCService) {
    95  	p.once.Do(func() {
    96  		p.implement(p, v)
    97  		p.rpc = v
    98  	})
    99  }
   100  
   101  // Get gets rpc service instance.
   102  func (p *Proxy) Get() common.RPCService {
   103  	return p.rpc
   104  }
   105  
   106  // GetCallback gets callback.
   107  func (p *Proxy) GetCallback() interface{} {
   108  	return p.callback
   109  }
   110  
   111  // GetInvoker gets Invoker.
   112  func (p *Proxy) GetInvoker() protocol.Invoker {
   113  	return p.invoke
   114  }
   115  
   116  // DefaultProxyImplementFunc the default function for proxy impl
   117  func DefaultProxyImplementFunc(p *Proxy, v common.RPCService) {
   118  	// check parameters, incoming interface must be a elem's pointer.
   119  	valueOf := reflect.ValueOf(v)
   120  
   121  	valueOfElem := valueOf.Elem()
   122  
   123  	makeDubboCallProxy := func(methodName string, outs []reflect.Type) func(in []reflect.Value) []reflect.Value {
   124  		return func(in []reflect.Value) []reflect.Value {
   125  			var (
   126  				err            error
   127  				inv            *invocation_impl.RPCInvocation
   128  				inIArr         []interface{}
   129  				inVArr         []reflect.Value
   130  				reply          reflect.Value
   131  				replyEmptyFlag bool
   132  			)
   133  			if methodName == "Echo" {
   134  				methodName = "$echo"
   135  			}
   136  
   137  			if len(outs) == 2 { // return (reply, error)
   138  				if outs[0].Kind() == reflect.Ptr {
   139  					reply = reflect.New(outs[0].Elem())
   140  				} else {
   141  					reply = reflect.New(outs[0])
   142  				}
   143  			} else { // only return error
   144  				replyEmptyFlag = true
   145  			}
   146  
   147  			start := 0
   148  			end := len(in)
   149  			invCtx := context.Background()
   150  			// retrieve the context from the first argument if existed
   151  			if end > 0 {
   152  				if in[0].Type().String() == "context.Context" {
   153  					if !in[0].IsNil() {
   154  						// the user declared context as method's parameter
   155  						invCtx = in[0].Interface().(context.Context)
   156  					}
   157  					start += 1
   158  				}
   159  			}
   160  
   161  			if end-start <= 0 {
   162  				inIArr = []interface{}{}
   163  				inVArr = []reflect.Value{}
   164  			} else if v, ok := in[start].Interface().([]interface{}); ok && end-start == 1 {
   165  				inIArr = v
   166  				inVArr = []reflect.Value{in[start]}
   167  			} else {
   168  				inIArr = make([]interface{}, end-start)
   169  				inVArr = make([]reflect.Value, end-start)
   170  				index := 0
   171  				for i := start; i < end; i++ {
   172  					inIArr[index] = in[i].Interface()
   173  					inVArr[index] = in[i]
   174  					index++
   175  				}
   176  			}
   177  
   178  			inv = invocation_impl.NewRPCInvocationWithOptions(invocation_impl.WithMethodName(methodName),
   179  				invocation_impl.WithArguments(inIArr),
   180  				invocation_impl.WithCallBack(p.callback), invocation_impl.WithParameterValues(inVArr))
   181  			if !replyEmptyFlag {
   182  				inv.SetReply(reply.Interface())
   183  			}
   184  
   185  			for k, value := range p.attachments {
   186  				inv.SetAttachment(k, value)
   187  			}
   188  
   189  			// add user setAttachment. It is compatibility with previous versions.
   190  			atm := invCtx.Value(constant.AttachmentKey)
   191  			if m, ok := atm.(map[string]string); ok {
   192  				for k, value := range m {
   193  					inv.SetAttachment(k, value)
   194  				}
   195  			} else if m2, ok2 := atm.(map[string]interface{}); ok2 {
   196  				// it is support to transfer map[string]interface{}. It refers to dubbo-java 2.7.
   197  				for k, value := range m2 {
   198  					inv.SetAttachment(k, value)
   199  				}
   200  			}
   201  
   202  			result := p.invoke.Invoke(invCtx, inv)
   203  			err = result.Error()
   204  			// cause is raw user level error
   205  			cause := perrors.Cause(err)
   206  			if err != nil {
   207  				// if some error happened, it should be log some info in the separate file.
   208  				if throwabler, ok := cause.(java_exception.Throwabler); ok {
   209  					logger.Warnf("[CallProxy] invoke service throw exception: %v , stackTraceElements: %v", cause.Error(), throwabler.GetStackTrace())
   210  				} else {
   211  					// entire error is only for printing, do not return, because user would not want to deal with massive framework-level error message
   212  					logger.Warnf("[CallProxy] received rpc err: %v", err)
   213  				}
   214  			} else {
   215  				logger.Debugf("[CallProxy] received rpc result successfully: %s", result)
   216  			}
   217  			if len(outs) == 1 {
   218  				return []reflect.Value{reflect.ValueOf(&cause).Elem()}
   219  			}
   220  			if len(outs) == 2 && outs[0].Kind() != reflect.Ptr {
   221  				return []reflect.Value{reply.Elem(), reflect.ValueOf(&cause).Elem()}
   222  			}
   223  			return []reflect.Value{reply, reflect.ValueOf(&cause).Elem()}
   224  		}
   225  	}
   226  
   227  	if err := refectAndMakeObjectFunc(valueOfElem, makeDubboCallProxy); err != nil {
   228  		logger.Errorf("The type or combination type of RPCService %T must be a pointer of a struct. error is %s", v, err)
   229  		return
   230  	}
   231  }
   232  
   233  func refectAndMakeObjectFunc(valueOfElem reflect.Value, makeDubboCallProxy func(methodName string, outs []reflect.Type) func(in []reflect.Value) []reflect.Value) error {
   234  	typeOf := valueOfElem.Type()
   235  	// check incoming interface, incoming interface's elem must be a struct.
   236  	if typeOf.Kind() != reflect.Struct {
   237  		return errors.New("invalid type kind")
   238  	}
   239  	numField := valueOfElem.NumField()
   240  	for i := 0; i < numField; i++ {
   241  		t := typeOf.Field(i)
   242  		methodName := t.Tag.Get("dubbo")
   243  		if methodName == "" {
   244  			methodName = t.Name
   245  		}
   246  		f := valueOfElem.Field(i)
   247  		if f.Kind() == reflect.Func && f.IsValid() && f.CanSet() {
   248  			outNum := t.Type.NumOut()
   249  
   250  			if outNum != 1 && outNum != 2 {
   251  				logger.Warnf("method %s of mtype %v has wrong number of in out parameters %d; needs exactly 1/2",
   252  					t.Name, t.Type.String(), outNum)
   253  				continue
   254  			}
   255  
   256  			// The latest return type of the method must be error.
   257  			if returnType := t.Type.Out(outNum - 1); returnType != typError {
   258  				logger.Warnf("the latest return type %s of method %q is not error", returnType, t.Name)
   259  				continue
   260  			}
   261  
   262  			funcOuts := make([]reflect.Type, outNum)
   263  			for i := 0; i < outNum; i++ {
   264  				funcOuts[i] = t.Type.Out(i)
   265  			}
   266  
   267  			// do method proxy here:
   268  			f.Set(reflect.MakeFunc(f.Type(), makeDubboCallProxy(methodName, funcOuts)))
   269  			logger.Debugf("set method [%s]", methodName)
   270  		} else if f.IsValid() && f.CanSet() {
   271  			// for struct combination
   272  			valueOfSub := reflect.New(t.Type)
   273  			valueOfElemInterface := valueOfSub.Elem()
   274  			if valueOfElemInterface.Type().Kind() == reflect.Struct {
   275  				if err := refectAndMakeObjectFunc(valueOfElemInterface, makeDubboCallProxy); err != nil {
   276  					return err
   277  				}
   278  				f.Set(valueOfElemInterface)
   279  			}
   280  		}
   281  	}
   282  	return nil
   283  }