go-micro.dev/v5@v5.12.0/server/grpc/subscriber.go (about) 1 package grpc 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "runtime/debug" 8 "strings" 9 10 "go-micro.dev/v5/broker" 11 "go-micro.dev/v5/errors" 12 "go-micro.dev/v5/logger" 13 "go-micro.dev/v5/metadata" 14 "go-micro.dev/v5/registry" 15 "go-micro.dev/v5/server" 16 ) 17 18 const ( 19 subSig = "func(context.Context, interface{}) error" 20 ) 21 22 type handler struct { 23 method reflect.Value 24 reqType reflect.Type 25 ctxType reflect.Type 26 } 27 28 type subscriber struct { 29 topic string 30 rcvr reflect.Value 31 typ reflect.Type 32 subscriber interface{} 33 handlers []*handler 34 endpoints []*registry.Endpoint 35 opts server.SubscriberOptions 36 } 37 38 func newSubscriber(topic string, sub interface{}, opts ...server.SubscriberOption) server.Subscriber { 39 options := server.SubscriberOptions{ 40 AutoAck: true, 41 } 42 43 for _, o := range opts { 44 o(&options) 45 } 46 47 var endpoints []*registry.Endpoint 48 var handlers []*handler 49 50 if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func { 51 h := &handler{ 52 method: reflect.ValueOf(sub), 53 } 54 55 switch typ.NumIn() { 56 case 1: 57 h.reqType = typ.In(0) 58 case 2: 59 h.ctxType = typ.In(0) 60 h.reqType = typ.In(1) 61 } 62 63 handlers = append(handlers, h) 64 65 endpoints = append(endpoints, ®istry.Endpoint{ 66 Name: "Func", 67 Request: extractSubValue(typ), 68 Metadata: map[string]string{ 69 "topic": topic, 70 "subscriber": "true", 71 }, 72 }) 73 } else { 74 hdlr := reflect.ValueOf(sub) 75 name := reflect.Indirect(hdlr).Type().Name() 76 77 for m := 0; m < typ.NumMethod(); m++ { 78 method := typ.Method(m) 79 h := &handler{ 80 method: method.Func, 81 } 82 83 switch method.Type.NumIn() { 84 case 2: 85 h.reqType = method.Type.In(1) 86 case 3: 87 h.ctxType = method.Type.In(1) 88 h.reqType = method.Type.In(2) 89 } 90 91 handlers = append(handlers, h) 92 93 endpoints = append(endpoints, ®istry.Endpoint{ 94 Name: name + "." + method.Name, 95 Request: extractSubValue(method.Type), 96 Metadata: map[string]string{ 97 "topic": topic, 98 "subscriber": "true", 99 }, 100 }) 101 } 102 } 103 104 return &subscriber{ 105 rcvr: reflect.ValueOf(sub), 106 typ: reflect.TypeOf(sub), 107 topic: topic, 108 subscriber: sub, 109 handlers: handlers, 110 endpoints: endpoints, 111 opts: options, 112 } 113 } 114 115 func validateSubscriber(sub server.Subscriber) error { 116 typ := reflect.TypeOf(sub.Subscriber()) 117 var argType reflect.Type 118 119 if typ.Kind() == reflect.Func { 120 name := "Func" 121 switch typ.NumIn() { 122 case 2: 123 argType = typ.In(1) 124 default: 125 return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig) 126 } 127 if !isExportedOrBuiltinType(argType) { 128 return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType) 129 } 130 if typ.NumOut() != 1 { 131 return fmt.Errorf("subscriber %v has wrong number of outs: %v require signature %s", 132 name, typ.NumOut(), subSig) 133 } 134 if returnType := typ.Out(0); returnType != typeOfError { 135 return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String()) 136 } 137 } else { 138 hdlr := reflect.ValueOf(sub.Subscriber()) 139 name := reflect.Indirect(hdlr).Type().Name() 140 141 for m := 0; m < typ.NumMethod(); m++ { 142 method := typ.Method(m) 143 144 switch method.Type.NumIn() { 145 case 3: 146 argType = method.Type.In(2) 147 default: 148 return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s", 149 name, method.Name, method.Type.NumIn(), subSig) 150 } 151 152 if !isExportedOrBuiltinType(argType) { 153 return fmt.Errorf("%v argument type not exported: %v", name, argType) 154 } 155 if method.Type.NumOut() != 1 { 156 return fmt.Errorf( 157 "subscriber %v.%v has wrong number of outs: %v require signature %s", 158 name, method.Name, method.Type.NumOut(), subSig) 159 } 160 if returnType := method.Type.Out(0); returnType != typeOfError { 161 return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String()) 162 } 163 } 164 } 165 166 return nil 167 } 168 169 func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broker.Handler { 170 return func(p broker.Event) (err error) { 171 defer func() { 172 if r := recover(); r != nil { 173 g.opts.Logger.Log(logger.ErrorLevel, "panic recovered: ", r) 174 g.opts.Logger.Log(logger.ErrorLevel, string(debug.Stack())) 175 err = errors.InternalServerError("go.micro.server", "panic recovered: %v", r) 176 } 177 }() 178 179 msg := p.Message() 180 // if we don't have headers, create empty map 181 if msg.Header == nil { 182 msg.Header = make(map[string]string) 183 } 184 185 ct := msg.Header["Content-Type"] 186 if len(ct) == 0 { 187 msg.Header["Content-Type"] = defaultContentType 188 ct = defaultContentType 189 } 190 cf, err := g.newGRPCCodec(ct) 191 if err != nil { 192 return err 193 } 194 195 hdr := make(map[string]string, len(msg.Header)) 196 for k, v := range msg.Header { 197 hdr[k] = v 198 } 199 delete(hdr, "Content-Type") 200 ctx := metadata.NewContext(context.Background(), hdr) 201 202 results := make(chan error, len(sb.handlers)) 203 204 for i := 0; i < len(sb.handlers); i++ { 205 handler := sb.handlers[i] 206 207 var isVal bool 208 var req reflect.Value 209 210 if handler.reqType.Kind() == reflect.Ptr { 211 req = reflect.New(handler.reqType.Elem()) 212 } else { 213 req = reflect.New(handler.reqType) 214 isVal = true 215 } 216 if isVal { 217 req = req.Elem() 218 } 219 220 if err = cf.Unmarshal(msg.Body, req.Interface()); err != nil { 221 return err 222 } 223 224 fn := func(ctx context.Context, msg server.Message) error { 225 var vals []reflect.Value 226 if sb.typ.Kind() != reflect.Func { 227 vals = append(vals, sb.rcvr) 228 } 229 if handler.ctxType != nil { 230 vals = append(vals, reflect.ValueOf(ctx)) 231 } 232 233 vals = append(vals, reflect.ValueOf(msg.Payload())) 234 235 returnValues := handler.method.Call(vals) 236 if rerr := returnValues[0].Interface(); rerr != nil { 237 return rerr.(error) 238 } 239 return nil 240 } 241 242 for i := len(opts.SubWrappers); i > 0; i-- { 243 fn = opts.SubWrappers[i-1](fn) 244 } 245 246 if g.wg != nil { 247 g.wg.Add(1) 248 } 249 go func() { 250 if g.wg != nil { 251 defer g.wg.Done() 252 } 253 err := fn(ctx, &rpcMessage{ 254 topic: sb.topic, 255 contentType: ct, 256 payload: req.Interface(), 257 header: msg.Header, 258 body: msg.Body, 259 }) 260 results <- err 261 }() 262 } 263 var errors []string 264 for i := 0; i < len(sb.handlers); i++ { 265 if rerr := <-results; rerr != nil { 266 errors = append(errors, rerr.Error()) 267 } 268 } 269 if len(errors) > 0 { 270 err = fmt.Errorf("subscriber error: %s", strings.Join(errors, "\n")) 271 } 272 273 return err 274 } 275 } 276 277 func (s *subscriber) Topic() string { 278 return s.topic 279 } 280 281 func (s *subscriber) Subscriber() interface{} { 282 return s.subscriber 283 } 284 285 func (s *subscriber) Endpoints() []*registry.Endpoint { 286 return s.endpoints 287 } 288 289 func (s *subscriber) Options() server.SubscriberOptions { 290 return s.opts 291 }