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 }