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  }