github.com/micro/go-micro/v2@v2.9.1/server/grpc/subscriber.go (about) 1 package grpc 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "runtime/debug" 8 "strings" 9 10 "github.com/micro/go-micro/v2/broker" 11 "github.com/micro/go-micro/v2/errors" 12 "github.com/micro/go-micro/v2/logger" 13 "github.com/micro/go-micro/v2/metadata" 14 "github.com/micro/go-micro/v2/registry" 15 "github.com/micro/go-micro/v2/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 172 defer func() { 173 if r := recover(); r != nil { 174 if logger.V(logger.ErrorLevel, logger.DefaultLogger) { 175 logger.Error("panic recovered: ", r) 176 logger.Error(string(debug.Stack())) 177 } 178 err = errors.InternalServerError("go.micro.server", "panic recovered: %v", r) 179 } 180 }() 181 182 msg := p.Message() 183 // if we don't have headers, create empty map 184 if msg.Header == nil { 185 msg.Header = make(map[string]string) 186 } 187 188 ct := msg.Header["Content-Type"] 189 if len(ct) == 0 { 190 msg.Header["Content-Type"] = defaultContentType 191 ct = defaultContentType 192 } 193 cf, err := g.newGRPCCodec(ct) 194 if err != nil { 195 return err 196 } 197 198 hdr := make(map[string]string, len(msg.Header)) 199 for k, v := range msg.Header { 200 hdr[k] = v 201 } 202 delete(hdr, "Content-Type") 203 ctx := metadata.NewContext(context.Background(), hdr) 204 205 results := make(chan error, len(sb.handlers)) 206 207 for i := 0; i < len(sb.handlers); i++ { 208 handler := sb.handlers[i] 209 210 var isVal bool 211 var req reflect.Value 212 213 if handler.reqType.Kind() == reflect.Ptr { 214 req = reflect.New(handler.reqType.Elem()) 215 } else { 216 req = reflect.New(handler.reqType) 217 isVal = true 218 } 219 if isVal { 220 req = req.Elem() 221 } 222 223 if err = cf.Unmarshal(msg.Body, req.Interface()); err != nil { 224 return err 225 } 226 227 fn := func(ctx context.Context, msg server.Message) error { 228 var vals []reflect.Value 229 if sb.typ.Kind() != reflect.Func { 230 vals = append(vals, sb.rcvr) 231 } 232 if handler.ctxType != nil { 233 vals = append(vals, reflect.ValueOf(ctx)) 234 } 235 236 vals = append(vals, reflect.ValueOf(msg.Payload())) 237 238 returnValues := handler.method.Call(vals) 239 if rerr := returnValues[0].Interface(); rerr != nil { 240 return rerr.(error) 241 } 242 return nil 243 } 244 245 for i := len(opts.SubWrappers); i > 0; i-- { 246 fn = opts.SubWrappers[i-1](fn) 247 } 248 249 if g.wg != nil { 250 g.wg.Add(1) 251 } 252 go func() { 253 if g.wg != nil { 254 defer g.wg.Done() 255 } 256 err := fn(ctx, &rpcMessage{ 257 topic: sb.topic, 258 contentType: ct, 259 payload: req.Interface(), 260 header: msg.Header, 261 body: msg.Body, 262 }) 263 results <- err 264 }() 265 } 266 var errors []string 267 for i := 0; i < len(sb.handlers); i++ { 268 if rerr := <-results; rerr != nil { 269 errors = append(errors, rerr.Error()) 270 } 271 } 272 if len(errors) > 0 { 273 err = fmt.Errorf("subscriber error: %s", strings.Join(errors, "\n")) 274 } 275 276 return err 277 } 278 } 279 280 func (s *subscriber) Topic() string { 281 return s.topic 282 } 283 284 func (s *subscriber) Subscriber() interface{} { 285 return s.subscriber 286 } 287 288 func (s *subscriber) Endpoints() []*registry.Endpoint { 289 return s.endpoints 290 } 291 292 func (s *subscriber) Options() server.SubscriberOptions { 293 return s.opts 294 }