github.com/wfusion/gofusion@v1.1.14/mq/construct.go (about)

     1  package mq
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"reflect"
     7  	"sync"
     8  	"syscall"
     9  
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/wfusion/gofusion/common/di"
    13  	"github.com/wfusion/gofusion/common/infra/watermill"
    14  	"github.com/wfusion/gofusion/common/utils"
    15  	"github.com/wfusion/gofusion/common/utils/inspect"
    16  	"github.com/wfusion/gofusion/config"
    17  
    18  	fusLog "github.com/wfusion/gofusion/log"
    19  
    20  	_ "github.com/wfusion/gofusion/log/customlogger"
    21  )
    22  
    23  var (
    24  	locker      sync.RWMutex
    25  	subscribers = map[string]map[string]Subscriber{}
    26  	publishers  = map[string]map[string]Publisher{}
    27  	routers     = map[string]map[string]IRouter{}
    28  )
    29  
    30  func Construct(ctx context.Context, confs map[string]*Conf, opts ...utils.OptionExtender) func() {
    31  	opt := utils.ApplyOptions[config.InitOption](opts...)
    32  	optU := utils.ApplyOptions[useOption](opts...)
    33  	if opt.AppName == "" {
    34  		opt.AppName = optU.appName
    35  	}
    36  
    37  	for name, conf := range confs {
    38  		addInstance(ctx, name, conf, opt)
    39  	}
    40  
    41  	return func() {
    42  		locker.Lock()
    43  		defer locker.Unlock()
    44  
    45  		pid := syscall.Getpid()
    46  		app := config.Use(opt.AppName).AppName()
    47  		if routers != nil {
    48  			for name, router := range routers[opt.AppName] {
    49  				log.Printf("%v [Gofusion] %s %s %s router exiting...",
    50  					pid, app, config.ComponentMessageQueue, name)
    51  				if err := router.close(); err == nil {
    52  					log.Printf("%v [Gofusion] %s %s %s router exited",
    53  						pid, app, config.ComponentMessageQueue, name)
    54  				} else {
    55  					log.Printf("%v [Gofusion] %s %s %s router exit failed: %s",
    56  						pid, app, config.ComponentMessageQueue, name, err)
    57  				}
    58  			}
    59  			delete(routers, opt.AppName)
    60  		}
    61  
    62  		if publishers != nil {
    63  			for name, publisher := range publishers[opt.AppName] {
    64  				log.Printf("%v [Gofusion] %s %s %s publisher exiting...",
    65  					pid, app, config.ComponentMessageQueue, name)
    66  				if err := publisher.close(); err == nil {
    67  					log.Printf("%v [Gofusion] %s %s %s publisher exited",
    68  						pid, app, config.ComponentMessageQueue, name)
    69  				} else {
    70  					log.Printf("%v [Gofusion] %s %s %s publisher exit failed: %s",
    71  						pid, app, config.ComponentMessageQueue, name, err)
    72  				}
    73  			}
    74  			delete(publishers, opt.AppName)
    75  		}
    76  
    77  		if subscribers != nil {
    78  			for name, subscriber := range subscribers[opt.AppName] {
    79  				log.Printf("%v [Gofusion] %s %s %s subscriber exiting...",
    80  					pid, app, config.ComponentMessageQueue, name)
    81  				if err := subscriber.close(); err == nil {
    82  					log.Printf("%v [Gofusion] %s %s %s subscriber exited",
    83  						pid, app, config.ComponentMessageQueue, name)
    84  				} else {
    85  					log.Printf("%v [Gofusion] %s %s %s subscriber exit failed: %s",
    86  						pid, app, config.ComponentMessageQueue, name, err)
    87  				}
    88  			}
    89  			delete(subscribers, opt.AppName)
    90  		}
    91  	}
    92  }
    93  
    94  func addInstance(ctx context.Context, name string, conf *Conf, opt *config.InitOption) {
    95  	var logger watermill.LoggerAdapter
    96  	if utils.IsStrNotBlank(conf.Logger) {
    97  		loggerType := inspect.TypeOf(conf.Logger)
    98  		loggerValue := reflect.New(loggerType)
    99  		if loggerValue.Type().Implements(customLoggerType) {
   100  			l := fusLog.Use(conf.LogInstance, fusLog.AppName(opt.AppName))
   101  			loggerValue.Interface().(customLogger).Init(l, opt.AppName, name)
   102  		}
   103  		logger = loggerValue.Convert(watermillLoggerType).Interface().(watermill.LoggerAdapter)
   104  	}
   105  
   106  	if conf.ConsumerConcurrency < 1 {
   107  		conf.ConsumerConcurrency = 1
   108  	}
   109  
   110  	var (
   111  		puber Publisher
   112  		suber Subscriber
   113  	)
   114  	newFunc, ok := newFn[conf.Type]
   115  	if ok {
   116  		puber, suber = newFunc(ctx, opt.AppName, name, conf, logger)
   117  	} else {
   118  		panic(errors.Errorf("unknown message queue type: %+v", conf.Type))
   119  	}
   120  
   121  	locker.Lock()
   122  	defer locker.Unlock()
   123  	if suber != nil {
   124  		if subscribers == nil {
   125  			subscribers = make(map[string]map[string]Subscriber)
   126  		}
   127  		if subscribers[opt.AppName] == nil {
   128  			subscribers[opt.AppName] = make(map[string]Subscriber)
   129  		}
   130  		if _, ok := subscribers[name]; ok {
   131  			panic(ErrDuplicatedSubscriberName)
   132  		}
   133  		subscribers[opt.AppName][name] = suber
   134  
   135  		if routers == nil {
   136  			routers = make(map[string]map[string]IRouter)
   137  		}
   138  		if routers[opt.AppName] == nil {
   139  			routers[opt.AppName] = make(map[string]IRouter)
   140  		}
   141  		if _, ok := routers[opt.AppName][name]; ok {
   142  			panic(ErrDuplicatedRouterName)
   143  		}
   144  		routers[opt.AppName][name] = newRouter(ctx, opt.AppName, name, conf, puber, suber, logger)
   145  
   146  		// ioc
   147  		if opt.DI != nil {
   148  			opt.DI.
   149  				MustProvide(func() Subscriber { return sub(name, AppName(opt.AppName)) }, di.Name(name)).
   150  				MustProvide(func() IRouter { return Use(name, AppName(opt.AppName)) }, di.Name(name))
   151  		}
   152  
   153  	}
   154  
   155  	if puber != nil {
   156  		if publishers == nil {
   157  			publishers = make(map[string]map[string]Publisher)
   158  		}
   159  		if publishers[opt.AppName] == nil {
   160  			publishers[opt.AppName] = make(map[string]Publisher)
   161  		}
   162  		if _, ok := publishers[opt.AppName][name]; ok {
   163  			panic(ErrDuplicatedPublisherName)
   164  		}
   165  		publishers[opt.AppName][name] = puber
   166  
   167  		// ioc
   168  		if opt.DI != nil {
   169  			opt.DI.MustProvide(func() Publisher { return Pub(name, AppName(opt.AppName)) }, di.Name(name))
   170  		}
   171  	}
   172  }
   173  
   174  type useOption struct {
   175  	appName string
   176  }
   177  
   178  func AppName(name string) utils.OptionFunc[useOption] {
   179  	return func(o *useOption) {
   180  		o.appName = name
   181  	}
   182  }
   183  
   184  func sub(name string, opts ...utils.OptionExtender) Subscriber {
   185  	opt := utils.ApplyOptions[useOption](opts...)
   186  
   187  	locker.RLock()
   188  	defer locker.RUnlock()
   189  	subscribers, ok := subscribers[opt.appName]
   190  	if !ok {
   191  		panic(errors.Errorf("mq subscriber instance not found for app: %s", opt.appName))
   192  	}
   193  	subscriber, ok := subscribers[name]
   194  	if !ok {
   195  		panic(errors.Errorf("mq subscriber instance not found for name: %s", name))
   196  	}
   197  	return subscriber
   198  }
   199  
   200  func Pub(name string, opts ...utils.OptionExtender) Publisher {
   201  	opt := utils.ApplyOptions[useOption](opts...)
   202  
   203  	locker.RLock()
   204  	defer locker.RUnlock()
   205  	publishers, ok := publishers[opt.appName]
   206  	if !ok {
   207  		panic(errors.Errorf("mq publisher instance not found for app: %s", opt.appName))
   208  	}
   209  	publisher, ok := publishers[name]
   210  	if !ok {
   211  		panic(errors.Errorf("mq publisher instance not found for name: %s", name))
   212  	}
   213  	return publisher
   214  }
   215  
   216  func Sub(name string, opts ...utils.OptionExtender) Subscriber {
   217  	opt := utils.ApplyOptions[useOption](opts...)
   218  
   219  	locker.RLock()
   220  	defer locker.RUnlock()
   221  	subscribers, ok := subscribers[opt.appName]
   222  	if !ok {
   223  		panic(errors.Errorf("mq subscriber instance not found for app: %s", opt.appName))
   224  	}
   225  	subscriber, ok := subscribers[name]
   226  	if !ok {
   227  		panic(errors.Errorf("mq subscriber instance not found for name: %s", name))
   228  	}
   229  	return subscriber
   230  }
   231  
   232  func Use(name string, opts ...utils.OptionExtender) IRouter {
   233  	opt := utils.ApplyOptions[useOption](opts...)
   234  
   235  	locker.RLock()
   236  	defer locker.RUnlock()
   237  	routers, ok := routers[opt.appName]
   238  	if !ok {
   239  		panic(errors.Errorf("mq router instance not found for app: %s", opt.appName))
   240  	}
   241  	r, ok := routers[name]
   242  	if !ok {
   243  		panic(errors.Errorf("mq router instance not found for name: %s", name))
   244  	}
   245  	return r
   246  }
   247  
   248  func init() {
   249  	config.AddComponent(config.ComponentMessageQueue, Construct, config.WithFlag(&flagString))
   250  }