github.com/wfusion/gofusion@v1.1.14/routine/construct.go (about) 1 package routine 2 3 import ( 4 "context" 5 "log" 6 "reflect" 7 "syscall" 8 "time" 9 10 "github.com/panjf2000/ants/v2" 11 "go.uber.org/atomic" 12 13 "github.com/wfusion/gofusion/common/utils" 14 "github.com/wfusion/gofusion/common/utils/inspect" 15 "github.com/wfusion/gofusion/config" 16 17 fusLog "github.com/wfusion/gofusion/log" 18 19 _ "github.com/wfusion/gofusion/log/customlogger" 20 ) 21 22 const ( 23 defaultMaxPoolSize = 10000000 24 ) 25 26 func Construct(ctx context.Context, conf Conf, opts ...utils.OptionExtender) func() { 27 opt := utils.ApplyOptions[config.InitOption](opts...) 28 optU := utils.ApplyOptions[candyOption](opts...) 29 if opt.AppName == "" { 30 opt.AppName = optU.appName 31 } 32 33 if conf.MaxRoutineAmount <= 0 { 34 conf.MaxRoutineAmount = defaultMaxPoolSize 35 } 36 37 rwlock.Lock() 38 defer rwlock.Unlock() 39 if pools == nil { 40 pools = make(map[string]map[string]Pool) 41 } 42 if pools[opt.AppName] == nil { 43 pools[opt.AppName] = make(map[string]Pool) 44 } 45 if ignored == nil { 46 ignored = make(map[string]*atomic.Int64) 47 } 48 if ignored[opt.AppName] == nil { 49 ignored[opt.AppName] = atomic.NewInt64(0) 50 } 51 if idles == nil { 52 idles = make(map[string]*atomic.Int64) 53 } 54 if idles[opt.AppName] == nil { 55 idles[opt.AppName] = atomic.NewInt64(int64(conf.MaxRoutineAmount)) 56 } 57 if utils.IsStrNotBlank(conf.Logger) { 58 if defaultLogger == nil { 59 defaultLogger = make(map[string]ants.Logger) 60 } 61 if defaultLogger[opt.AppName] == nil { 62 logger := reflect.New(inspect.TypeOf(conf.Logger)).Interface().(ants.Logger) 63 defaultLogger[opt.AppName] = logger 64 if custom, ok := logger.(customLogger); ok { 65 l := fusLog.Use(conf.LogInstance, fusLog.AppName(opt.AppName)) 66 custom.Init(l, opt.AppName) 67 } 68 } 69 } 70 maxReleaseTime := utils.Must(time.ParseDuration(conf.MaxReleaseTimePerPool)) 71 72 go startDaemonRoutines(ctx, opt.AppName, &conf) 73 74 return func() { 75 rwlock.Lock() 76 defer rwlock.Unlock() 77 78 pid := syscall.Getpid() 79 app := config.Use(opt.AppName).AppName() 80 allExited := func() bool { 81 return idles[opt.AppName].Load() == int64(conf.MaxRoutineAmount)-ignored[opt.AppName].Load() 82 } 83 84 // waiting for pool 85 if pools != nil { 86 for name, pool := range pools[opt.AppName] { 87 if err := pool.ReleaseTimeout(maxReleaseTime, ignoreMutex()); err != nil { 88 log.Printf("%v [Gofusion] %s %s exit with releasing pool %s failed because %s", 89 pid, app, config.ComponentGoroutinePool, name, err) 90 } 91 delete(pools[opt.AppName], name) 92 } 93 } 94 95 log.Printf("%v [Gofusion] %s %s pool routines are recycled", pid, app, config.ComponentGoroutinePool) 96 97 // waiting for go 98 utils.Timeout(maxReleaseTime, utils.TimeoutWg(&wg)) 99 log.Printf("%v [Gofusion] %s %s go routines are recycled", pid, app, config.ComponentGoroutinePool) 100 101 if !allExited() { 102 log.Printf("%v [Gofusion] %s %s exit without all goroutines recycled [exists%v]", 103 pid, app, config.ComponentGoroutinePool, showRoutine(opt.AppName)) 104 } 105 106 delete(ignored, opt.AppName) 107 delete(idles, opt.AppName) 108 delete(routines, opt.AppName) 109 } 110 } 111 112 func configs(appName string) (conf Conf) { 113 _ = config.Use(appName).LoadComponentConfig(config.ComponentGoroutinePool, &conf) 114 return 115 } 116 117 func forceSync(appName string) bool { 118 return configs(appName).ForceSync 119 } 120 121 func init() { 122 config.AddComponent(config.ComponentGoroutinePool, Construct, config.WithFlag(&flagString)) 123 }