github.com/wfusion/gofusion@v1.1.14/mq/router.go (about) 1 package mq 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/PaesslerAG/gval" 12 "github.com/pkg/errors" 13 "github.com/sony/gobreaker" 14 "go.uber.org/multierr" 15 16 "github.com/wfusion/gofusion/common/infra/watermill" 17 "github.com/wfusion/gofusion/common/infra/watermill/message/router/middleware" 18 "github.com/wfusion/gofusion/common/infra/watermill/message/router/plugin" 19 "github.com/wfusion/gofusion/common/utils" 20 "github.com/wfusion/gofusion/common/utils/compress" 21 "github.com/wfusion/gofusion/common/utils/inspect" 22 "github.com/wfusion/gofusion/common/utils/serialize" 23 "github.com/wfusion/gofusion/log" 24 "github.com/wfusion/gofusion/routine" 25 26 mw "github.com/wfusion/gofusion/common/infra/watermill/message" 27 fusCtx "github.com/wfusion/gofusion/context" 28 pd "github.com/wfusion/gofusion/internal/util/payload" 29 ) 30 31 const ( 32 defaultRouterEventHandlerName = "__router_event_handler" 33 ) 34 35 type router struct { 36 *mw.Router 37 38 appName string 39 40 c *Conf 41 pub Publisher 42 sub Subscriber 43 44 compressType compress.Algorithm 45 serializeType serialize.Algorithm 46 47 ctx context.Context 48 49 locker sync.RWMutex 50 eventHandlers map[string]*handler 51 eventSubscriberHandlers map[string]*handler 52 } 53 type handler struct { 54 fn reflect.Value 55 evtType reflect.Type 56 evtPayloadType reflect.Type 57 } 58 59 func newRouter(ctx context.Context, appName, name string, conf *Conf, 60 pub Publisher, sub Subscriber, logger watermill.LoggerAdapter) *router { 61 r := utils.Must(mw.NewRouter(mw.RouterConfig{CloseTimeout: 15 * time.Second}, logger)) 62 r.AddPlugin(plugin.SignalsHandler) 63 r.AddMiddleware( 64 middleware.Recoverer, 65 middleware.CorrelationID, 66 ) 67 for _, mwsConf := range conf.ConsumeMiddlewares { 68 switch mwsConf.Type { 69 case middlewareTypeRetry: 70 r.AddMiddleware(middleware.Retry{ 71 MaxRetries: mwsConf.RetryMaxRetries, 72 InitialInterval: utils.Must(time.ParseDuration(mwsConf.RetryInitialInterval)), 73 MaxInterval: utils.Must(time.ParseDuration(mwsConf.RetryMaxInterval)), 74 Multiplier: mwsConf.RetryMultiplier, 75 MaxElapsedTime: utils.Must(time.ParseDuration(mwsConf.RetryMaxElapsedTime)), 76 RandomizationFactor: mwsConf.RetryRandomizationFactor, 77 OnRetryHook: func(attempt int, delay time.Duration) { 78 logTrace(ctx, logger, appName, name, 79 "retry to consume message [attempt[%v] delay[%s]]", attempt, delay) 80 }, 81 Logger: logger, 82 }.Middleware) 83 case middlewareTypeThrottle: 84 r.AddMiddleware( 85 middleware.NewThrottle( 86 int64(mwsConf.ThrottleCount), 87 utils.Must(time.ParseDuration(mwsConf.ThrottleDuration)), 88 ).Middleware, 89 ) 90 case middlewareTypeInstanceAck: 91 r.AddMiddleware(middleware.InstantAck) 92 case middlewareTypePoison: 93 shouldGoToPoisonQueue := func(err error) bool { return err != nil } 94 r.AddMiddleware( 95 utils.Must(middleware.PoisonQueueWithFilter( 96 pub.watermillPublisher(), 97 mwsConf.PoisonTopic, 98 shouldGoToPoisonQueue, 99 )), 100 ) 101 case middlewareTypeTimeout: 102 r.AddMiddleware(middleware.Timeout(utils.Must(time.ParseDuration(mwsConf.Timeout)))) 103 case middlewareTypeCircuitBreaker: 104 var expr gval.Evaluable 105 if utils.IsStrNotBlank(mwsConf.CircuitBreakerTripExpr) { 106 expr = utils.Must(gval.Full().NewEvaluable(mwsConf.CircuitBreakerTripExpr)) 107 } 108 r.AddMiddleware(middleware.NewCircuitBreaker(gobreaker.Settings{ 109 Name: name, 110 MaxRequests: uint32(mwsConf.CircuitBreakerMaxRequests), 111 Interval: utils.Must(time.ParseDuration(mwsConf.CircuitBreakerInterval)), 112 Timeout: utils.Must(time.ParseDuration(mwsConf.CircuitBreakerTimeout)), 113 ReadyToTrip: func(counts gobreaker.Counts) bool { 114 // fallback to default ready to trip expression 115 if expr == nil { 116 return counts.ConsecutiveFailures > 5 117 } 118 if ok, err := expr.EvalBool(ctx, map[string]uint32{ 119 "requests": counts.Requests, 120 "total_successes": counts.TotalSuccesses, 121 "total_failures": counts.TotalFailures, 122 "consecutive_successes": counts.ConsecutiveSuccesses, 123 "consecutive_failures": counts.ConsecutiveFailures, 124 }); err == nil { 125 return ok 126 } 127 // fallback to default ready to trip expression 128 return counts.ConsecutiveFailures > 5 129 }, 130 OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) { 131 logInfo(ctx, logger, appName, name, "circuit breaker state changed: %s -> %s", from, to) 132 }, 133 IsSuccessful: func(err error) bool { return err == nil }, 134 }).Middleware) 135 default: 136 typ := inspect.TypeOf(string(mwsConf.Type)) 137 if typ == nil || typ.ConvertibleTo(watermillHandlerMiddlewareType) { 138 panic(errors.Errorf("unknown mq middleware: %s", mwsConf.Type)) 139 } 140 mws := reflect.New(typ).Elem().Convert(watermillHandlerMiddlewareType).Interface() 141 r.AddMiddleware(mws.(mw.HandlerMiddleware)) 142 } 143 } 144 145 rr := &router{ 146 ctx: ctx, 147 appName: appName, 148 c: conf, 149 pub: pub, 150 sub: sub, 151 Router: r, 152 eventHandlers: make(map[string]*handler), 153 eventSubscriberHandlers: make(map[string]*handler), 154 } 155 rr.serializeType = serialize.ParseAlgorithm(conf.SerializeType) 156 rr.compressType = compress.ParseAlgorithm(conf.CompressType) 157 158 return rr 159 } 160 161 func (r *router) Handle(handlerName string, hdr any, opts ...utils.OptionExtender) { 162 opt := utils.ApplyOptions[routerOption](opts...) 163 if opt.isEventSubscriber || singleConsumerMQType.Contains(r.c.Type) { 164 r.addHandler(handlerName, handlerName, hdr, opt) 165 return 166 } 167 for i := 0; i < r.c.ConsumerConcurrency; i++ { 168 consumerName := fmt.Sprintf("%s_%v", handlerName, i) 169 r.addHandler(handlerName, consumerName, hdr, opt) 170 } 171 } 172 173 func (r *router) Serve() (err error) { 174 if r.isHandlerConflict() { 175 panic(ErrEventHandlerConflict) 176 } 177 if len(r.eventHandlers) > 0 { 178 r.runEventHandlers() 179 } 180 if err = r.run(); err != nil { 181 return 182 } 183 <-r.Router.ClosedCh 184 return 185 } 186 187 func (r *router) Start() { 188 if r.isHandlerConflict() { 189 panic(ErrEventHandlerConflict) 190 } 191 if len(r.eventHandlers) > 0 { 192 r.runEventHandlers() 193 } 194 routine.Go(r.run, routine.AppName(r.appName)) 195 } 196 197 func (r *router) Running() <-chan struct{} { 198 return r.Router.Running() 199 } 200 201 func (r *router) run() (err error) { 202 if r.Router.IsRunning() { 203 return r.Router.RunHandlers(r.ctx) 204 } 205 if err = r.Router.Run(r.ctx); err != nil { 206 if errors.Is(err, mw.ErrRouterIsAlreadyRunning) { 207 return r.Router.RunHandlers(r.ctx) 208 } 209 } 210 return 211 } 212 213 func (r *router) addHandler(handlerName, consumerName string, hdr any, opt *routerOption) { 214 switch fn := hdr.(type) { 215 case HandlerFunc: 216 r.Router.AddNoPublisherHandler( 217 consumerName, 218 r.sub.topic(), 219 r.sub.watermillSubscriber(), 220 func(wmsg *mw.Message) (err error) { 221 msg, err := messageConvertFrom(wmsg, r.serializeType, r.compressType) 222 if err != nil { 223 return 224 } 225 return fn(msg) 226 }, 227 ) 228 case mw.NoPublishHandlerFunc: 229 r.Router.AddNoPublisherHandler( 230 consumerName, 231 r.sub.topic(), 232 r.sub.watermillSubscriber(), 233 fn, 234 ) 235 case mw.HandlerFunc: 236 r.Router.AddHandler( 237 consumerName, 238 r.sub.topic(), 239 r.sub.watermillSubscriber(), 240 r.pub.topic(), 241 r.pub.watermillPublisher(), 242 fn, 243 ) 244 default: 245 fnVal := reflect.ValueOf(hdr) 246 switch { 247 case fnVal.CanConvert(watermillHandlerFuncType): 248 r.Router.AddNoPublisherHandler( 249 consumerName, 250 r.sub.topic(), 251 r.sub.watermillSubscriber(), 252 func(msg *mw.Message) error { 253 rets := fnVal.Convert(watermillHandlerFuncType).Call( 254 []reflect.Value{reflect.ValueOf(rawMessageConvertFrom(msg))}, 255 ) 256 return utils.ParseVariadicFuncResult[error](rets, 0) 257 }, 258 ) 259 case fnVal.CanConvert(watermillNoPublishHandlerFuncType): 260 r.Router.AddNoPublisherHandler( 261 consumerName, 262 r.sub.topic(), 263 r.sub.watermillSubscriber(), 264 func(msg *mw.Message) error { 265 rets := fnVal. 266 Convert(watermillNoPublishHandlerFuncType). 267 Call([]reflect.Value{reflect.ValueOf(msg)}) 268 return utils.ParseVariadicFuncResult[error](rets, 0) 269 }, 270 ) 271 case fnVal.CanConvert(handlerFuncType): 272 r.Router.AddHandler( 273 consumerName, 274 r.sub.topic(), 275 r.sub.watermillSubscriber(), 276 r.pub.topic(), 277 r.pub.watermillPublisher(), 278 func(wmsg *mw.Message) (msgs []*mw.Message, err error) { 279 msg, err := messageConvertFrom(wmsg, r.serializeType, r.compressType) 280 if err != nil { 281 return 282 } 283 rets := fnVal.Convert(handlerFuncType).Call([]reflect.Value{reflect.ValueOf(msg)}) 284 msgs = utils.ParseVariadicFuncResult[[]*mw.Message](rets, 0) 285 err = utils.ParseVariadicFuncResult[error](rets, 0) 286 return 287 }, 288 ) 289 case isEventHandler(fnVal): 290 r.handleEvent(handlerName, fnVal, opt) 291 default: 292 r.Router.AddNoPublisherHandler( 293 consumerName, 294 r.sub.topic(), 295 r.sub.watermillSubscriber(), 296 r.handle(hdr), 297 ) 298 } 299 } 300 } 301 302 func (r *router) handleEvent(eventType string, fnVal reflect.Value, opt *routerOption) { 303 // FIXME: Translating generics to corresponding implemented generic types like this is too hacky. 304 // If this set becomes invalid, switch to the implementation of event payload as any without generics, 305 // and the router can continue to provide it using the current method of storing reflect.Type. 306 evtType := fnVal.Type().In(1) 307 eventName := strings.Replace(evtType.Name(), "Event[", "event[", 1) 308 eventTypeName := fmt.Sprintf(mqPackageSignFormat, eventName) 309 et := inspect.TypeOf(eventTypeName) 310 if et == nil { 311 panic(errors.Errorf("unknown event generic object type: %s", eventTypeName)) 312 } 313 eventPayloadName := strings.Replace(evtType.Name(), "Event[", "eventPayload[", 1) 314 eventPayloadTypeName := fmt.Sprintf(mqPackageSignFormat, eventPayloadName) 315 ept := inspect.TypeOf(eventPayloadTypeName) 316 if ept == nil { 317 panic(errors.Errorf("unknown event payload generic object type: %s", eventPayloadTypeName)) 318 } 319 320 hdr := &handler{ 321 fn: fnVal, 322 evtType: et, 323 evtPayloadType: reflect.PtrTo(ept), 324 } 325 326 r.locker.Lock() 327 defer r.locker.Unlock() 328 if !opt.isEventSubscriber { 329 r.eventHandlers[eventType] = hdr 330 } else { 331 r.eventSubscriberHandlers[eventType] = hdr 332 r.addEventDispatchHandler(defaultRouterEventHandlerName + "_" + eventType) 333 routine.Go(r.run, routine.AppName(r.appName)) 334 } 335 } 336 337 func (r *router) handle(hdr any) mw.NoPublishHandlerFunc { 338 typ := wrapParams(hdr) 339 fn := utils.WrapFunc1[error](hdr) 340 return func(msg *mw.Message) (err error) { 341 _, data, _, err := pd.Unseal(msg.Payload, 342 pd.Serialize(r.serializeType), pd.Compress(r.compressType), pd.Type(typ)) 343 if err != nil { 344 return 345 } 346 params := unwrapParams(typ, data) 347 ctx := fusCtx.New(fusCtx.Watermill(msg.Metadata)) 348 return fn(append([]any{ctx}, params...)...) 349 } 350 } 351 352 func (r *router) runEventHandlers() { 353 if singleConsumerMQType.Contains(r.c.Type) { 354 r.addEventDispatchHandler(defaultRouterEventHandlerName) 355 return 356 } 357 for i := 0; i < r.c.ConsumerConcurrency; i++ { 358 consumerName := fmt.Sprintf("%s_%v", defaultRouterEventHandlerName, i) 359 r.addEventDispatchHandler(consumerName) 360 } 361 } 362 363 func (r *router) addEventDispatchHandler(consumerName string) { 364 r.Router.AddHandler( 365 consumerName, 366 r.sub.topic(), 367 r.sub.watermillSubscriber(), 368 r.pub.topic(), 369 r.pub.watermillPublisher(), 370 func(msg *mw.Message) (pubMsgs []*mw.Message, err error) { 371 eventType := msg.Metadata[keyEventType] 372 r.locker.RLock() 373 hdr, ok1 := r.eventHandlers[eventType] 374 subhdr, ok2 := r.eventSubscriberHandlers[eventType] 375 r.locker.RUnlock() 376 if !ok1 && !ok2 { 377 rawID := "unknown" 378 if msg.Metadata != nil { 379 rawID = msg.Metadata[watermill.ContextKeyRawMessageID] 380 } 381 return nil, errors.Errorf( 382 "handle unknown event message [type[%s] message_uuid[%s] message_raw_id[%s]]", 383 eventType, msg.UUID, rawID) 384 } 385 handlers := []*handler{hdr, subhdr} 386 387 wg := new(sync.WaitGroup) 388 futures := make([]*routine.Future, 0, len(handlers)) 389 for _, hdr := range handlers { 390 if hdr == nil { 391 continue 392 } 393 wg.Add(1) 394 f := routine.Promise( 395 func(hdr handler) (msgs any, err error) { 396 _, data, _, err := pd.Unseal(msg.Payload, 397 pd.Serialize(r.serializeType), pd.Compress(r.compressType), pd.Type(hdr.evtPayloadType)) 398 if err != nil { 399 return 400 } 401 event := reflect.New(hdr.evtType).Interface() 402 inspect.SetField(event, "pd", data) 403 404 ctx := fusCtx.New(fusCtx.Watermill(msg.Metadata)) 405 ctx = log.SetCtxFields(ctx, log.Fields{ 406 keyEntityID: msg.Metadata[keyEntityID], 407 keyEventType: msg.Metadata[keyEventType], 408 }) 409 inspect.SetField(event, "ctx", ctx) 410 inspect.SetField(event, "ackfn", msg.Ack) 411 inspect.SetField(event, "nackfn", msg.Nack) 412 413 rets := hdr.fn.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(event)}) 414 msgs = utils.ParseVariadicFuncResult[[]Message](rets, 0) 415 err = utils.ParseVariadicFuncResult[error](rets, 0) 416 return 417 }, 418 true, 419 routine.Args(hdr), 420 routine.WaitGroup(wg), 421 routine.AppName(r.appName), 422 ) 423 futures = append(futures, f) 424 } 425 wg.Wait() 426 427 pubMsgs = make([]*mw.Message, 0, len(handlers)) 428 for _, f := range futures { 429 msgsAny, msgErr := f.Get() 430 err = multierr.Append(err, msgErr) 431 if msgsAny != nil { 432 msgs, _ := msgsAny.([]Message) 433 for _, m := range msgs { 434 pubMsgs = append(pubMsgs, messageConvertTo(m)) 435 } 436 } 437 } 438 return 439 }, 440 ) 441 } 442 443 func (r *router) isHandlerConflict() (conflict bool) { 444 if len(r.eventHandlers) == 0 { 445 return 446 } 447 for name := range r.Handlers() { 448 if !strings.Contains(name, defaultRouterEventHandlerName) { 449 return true 450 } 451 } 452 return 453 } 454 455 func (r *router) close() (err error) { 456 return r.Close() 457 }