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

     1  package async
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"sync"
     7  	"syscall"
     8  
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/wfusion/gofusion/common/di"
    12  	"github.com/wfusion/gofusion/common/utils"
    13  	"github.com/wfusion/gofusion/config"
    14  
    15  	_ "github.com/wfusion/gofusion/log/customlogger"
    16  )
    17  
    18  var (
    19  	locker    sync.RWMutex
    20  	consumers = map[string]map[string]Consumable{}
    21  	producers = map[string]map[string]Producable{}
    22  )
    23  
    24  func Construct(ctx context.Context, confs map[string]*Conf, opts ...utils.OptionExtender) func() {
    25  	opt := utils.ApplyOptions[config.InitOption](opts...)
    26  	optU := utils.ApplyOptions[useOption](opts...)
    27  	if opt.AppName == "" {
    28  		opt.AppName = optU.appName
    29  	}
    30  
    31  	for name, conf := range confs {
    32  		addInstance(ctx, name, conf, opt)
    33  	}
    34  	return func() {
    35  		locker.Lock()
    36  		defer locker.Unlock()
    37  
    38  		pid := syscall.Getpid()
    39  		app := config.Use(opt.AppName).AppName()
    40  		if consumers != nil {
    41  			for name, router := range consumers[opt.AppName] {
    42  				log.Printf("%v [Gofusion] %s %s %s exiting...", pid, app, config.ComponentAsync, name)
    43  				if err := router.shutdown(); err == nil {
    44  					log.Printf("%v [Gofusion] %s %s %s exited", pid, app, config.ComponentAsync, name)
    45  				} else {
    46  					log.Printf("%v [Gofusion] %s %s %s exit failed: %s", pid, app, config.ComponentAsync, name, err)
    47  				}
    48  			}
    49  			delete(consumers, opt.AppName)
    50  		}
    51  
    52  		if producers != nil {
    53  			producers[opt.AppName] = make(map[string]Producable, len(producers))
    54  		}
    55  	}
    56  }
    57  
    58  func addInstance(ctx context.Context, name string, conf *Conf, opt *config.InitOption) {
    59  	var (
    60  		producer Producable
    61  		consumer Consumable
    62  	)
    63  	switch conf.Type {
    64  	case asyncTypeAsynq:
    65  		if conf.Producer {
    66  			producer = newAsynqProducer(ctx, opt.AppName, name, conf)
    67  		}
    68  		if conf.Consumer {
    69  			consumer = newAsynqConsumer(ctx, opt.AppName, name, conf)
    70  		}
    71  	case asyncTypeMysql:
    72  		fallthrough
    73  	default:
    74  		panic(ErrUnsupportedSchedulerType)
    75  	}
    76  
    77  	locker.Lock()
    78  	defer locker.Unlock()
    79  	if consumer != nil {
    80  		if consumers == nil {
    81  			consumers = make(map[string]map[string]Consumable)
    82  		}
    83  		if consumers[opt.AppName] == nil {
    84  			consumers[opt.AppName] = make(map[string]Consumable)
    85  		}
    86  		if _, ok := consumers[name]; ok {
    87  			panic(ErrDuplicatedInstanceName)
    88  		}
    89  		consumers[opt.AppName][name] = consumer
    90  
    91  		// ioc
    92  		if opt.DI != nil {
    93  			opt.DI.MustProvide(
    94  				func() Consumable { return C(name, AppName(opt.AppName)) },
    95  				di.Name(name),
    96  			)
    97  		}
    98  	}
    99  
   100  	if producer != nil {
   101  		if producers == nil {
   102  			producers = make(map[string]map[string]Producable)
   103  		}
   104  		if producers[opt.AppName] == nil {
   105  			producers[opt.AppName] = make(map[string]Producable)
   106  		}
   107  		if _, ok := producers[name]; ok {
   108  			panic(ErrDuplicatedInstanceName)
   109  		}
   110  		producers[opt.AppName][name] = producer
   111  
   112  		// ioc
   113  		if opt.DI != nil {
   114  			opt.DI.MustProvide(
   115  				func() Producable { return P(name) },
   116  				di.Name(name),
   117  			)
   118  		}
   119  	}
   120  }
   121  
   122  type useOption struct {
   123  	appName string
   124  }
   125  
   126  func AppName(name string) utils.OptionFunc[useOption] {
   127  	return func(o *useOption) {
   128  		o.appName = name
   129  	}
   130  }
   131  
   132  func C(name string, opts ...utils.OptionExtender) Consumable {
   133  	opt := utils.ApplyOptions[useOption](opts...)
   134  
   135  	locker.RLock()
   136  	defer locker.RUnlock()
   137  	consumers, ok := consumers[opt.appName]
   138  	if !ok {
   139  		panic(errors.Errorf("async consumer instance not found for app: %s", opt.appName))
   140  	}
   141  	consumer, ok := consumers[name]
   142  	if !ok {
   143  		panic(errors.Errorf("async consumer instance not found for name: %s", name))
   144  	}
   145  	return consumer
   146  }
   147  
   148  func P(name string, opts ...utils.OptionExtender) Producable {
   149  	opt := utils.ApplyOptions[useOption](opts...)
   150  
   151  	locker.RLock()
   152  	defer locker.RUnlock()
   153  	producers, ok := producers[opt.appName]
   154  	if !ok {
   155  		panic(errors.Errorf("async producer instance not found for app: %s", opt.appName))
   156  	}
   157  	producer, ok := producers[name]
   158  	if !ok {
   159  		panic(errors.Errorf("async producer instance not found for name: %s", name))
   160  	}
   161  	return producer
   162  }
   163  
   164  func init() {
   165  	config.AddComponent(config.ComponentAsync, Construct, config.WithFlag(&flagString))
   166  }