github.com/sandwich-go/boost@v1.3.29/module/master.go (about)

     1  package module
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"github.com/sandwich-go/boost"
     7  	"github.com/sandwich-go/boost/version"
     8  	"github.com/sandwich-go/boost/xdebug"
     9  	"github.com/sandwich-go/boost/xdebug/race"
    10  	"github.com/sandwich-go/boost/xpanic"
    11  	"github.com/sandwich-go/boost/xsync"
    12  	"os"
    13  	"sync"
    14  	"time"
    15  )
    16  
    17  type agent struct {
    18  	master *master
    19  	Module
    20  	wg        sync.WaitGroup
    21  	closeChan chan struct{}
    22  }
    23  
    24  func (a *agent) run() {
    25  	a.master.runningCount.Add(1)
    26  	a.Run(a.closeChan)
    27  	a.wg.Done()
    28  	if a.master.runningCount.Add(-1) == 0 {
    29  		a.master.Stop(fmt.Sprintf("%s stopped, no module in running", a.Name()))
    30  	}
    31  }
    32  
    33  func (a *agent) close() {
    34  	xpanic.Do(func() {
    35  		a.OnClose()
    36  		boost.LogInfof("ModuleName %s closed", a.Name())
    37  	}, func(p *xpanic.Panic) {
    38  		boost.LogInfof("ModuleName %s closed with reason: %v", a.Name(), p.Reason)
    39  	})
    40  }
    41  
    42  // master Module管理器
    43  type master struct {
    44  	masterStarted      xsync.AtomicBool
    45  	timeoutDuration    time.Duration
    46  	allAgents          []*agent
    47  	runningCount       xsync.AtomicInt32
    48  	chanHasShutdown    chan struct{}
    49  	chanStoppedByLogic chan string // 逻辑导致的退出,用户主动停止,逻辑异常停止
    50  	plugins            []Plugin
    51  }
    52  
    53  // New 新建一个 Module 管理器,一般情况下使用默认 default 即可
    54  func New() *master {
    55  	return &master{
    56  		chanHasShutdown:    make(chan struct{}),
    57  		chanStoppedByLogic: make(chan string, 1),
    58  	}
    59  }
    60  
    61  func (m *master) Modules() []Module {
    62  	var out = make([]Module, 0, len(m.allAgents))
    63  	for i := 0; i < len(m.allAgents); i++ {
    64  		out = append(out, m.allAgents[i].Module)
    65  	}
    66  	return out
    67  }
    68  
    69  func (m *master) AttachPlugin(plugins ...Plugin) {
    70  	if len(plugins) > 0 {
    71  		m.plugins = append(m.plugins, plugins...)
    72  	}
    73  }
    74  
    75  func (m *master) afterRunModule(ctx context.Context) {
    76  	for _, v := range m.plugins {
    77  		v.AfterRunModule(ctx, m)
    78  	}
    79  }
    80  
    81  func (m *master) beforeCloseModule(ctx context.Context) {
    82  	for _, v := range m.plugins {
    83  		v.BeforeCloseModule(ctx, m)
    84  	}
    85  }
    86  
    87  func (m *master) registerOneModule(md Module) *agent {
    88  	a := &agent{Module: md, master: m, closeChan: make(chan struct{}, 1)}
    89  	m.allAgents = append(m.allAgents, a)
    90  	return a
    91  }
    92  
    93  // Register 注册多个Module
    94  func (m *master) Register(ms ...Module) {
    95  	for _, md := range ms {
    96  		_ = m.registerOneModule(md)
    97  	}
    98  }
    99  
   100  // Stop 主动停止Master
   101  func (m *master) Stop(reason ...string) {
   102  	var s = "stop_called"
   103  	if len(reason) > 0 {
   104  		s = reason[0]
   105  	}
   106  	select {
   107  	case <-m.chanStoppedByLogic:
   108  	default:
   109  	}
   110  	m.chanStoppedByLogic <- s
   111  }
   112  
   113  func (m *master) runAll() {
   114  	for i := 0; i < len(m.allAgents); i++ {
   115  		m.allAgents[i].OnInit()
   116  	}
   117  
   118  	for i := 0; i < len(m.allAgents); i++ {
   119  		boost.LogInfof("ModuleName %s starting ...", m.allAgents[i].Name())
   120  		m.allAgents[i].wg.Add(1)
   121  		go m.allAgents[i].run()
   122  		boost.LogInfof("ModuleName %s started ...", m.allAgents[i].Name())
   123  	}
   124  }
   125  
   126  // RunModule 运行一个单独的 module
   127  func (m *master) RunModule(md Module) {
   128  	s := m.registerOneModule(md)
   129  	if !m.masterStarted.Get() {
   130  		return
   131  	}
   132  	boost.LogInfof("ModuleName %s starting ...", s.Name())
   133  	s.wg.Add(1)
   134  	s.OnInit()
   135  	go s.run()
   136  	boost.LogInfof("ModuleName %s started", s.Name())
   137  }
   138  
   139  func (m *master) closeAll(ctx context.Context) {
   140  	for i := len(m.allAgents) - 1; i >= 0; i-- {
   141  		a := m.allAgents[i]
   142  		boost.LogInfof("ModuleName %s closing ...", a.Name())
   143  		close(a.closeChan)
   144  		if m.timeoutDuration == 0 {
   145  			a.wg.Wait()
   146  		} else {
   147  			if xsync.WaitContext(&a.wg, ctx) {
   148  				boost.LogInfof("ModuleName %s close with timeout %v ...", a.Name(), m.timeoutDuration)
   149  			}
   150  		}
   151  		a.close()
   152  	}
   153  }
   154  
   155  // RunWithCloseTimeout 参考Run,扩充了关闭超时支持,防止逻辑层堵塞导致进程关闭失败
   156  func (m *master) RunWithCloseTimeout(duration time.Duration, ms ...Module) {
   157  	m.timeoutDuration = duration
   158  	m.Run(ms...)
   159  }
   160  
   161  // ShutdownNotify master停止的通知信号
   162  func (m *master) ShutdownNotify() chan struct{} { return m.chanHasShutdown }
   163  
   164  // Run 运行入口,进程会堵塞在这里直到收到停止信号,可以指定要运行的Module列表
   165  func (m *master) Run(ms ...Module) {
   166  	m.Register(ms...)
   167  	m.runAll()
   168  
   169  	xdebug.CheckRequireDependencies()
   170  	boost.LogInfof("progress started, pid: %d, version: %s, race: %t, debug_enabled: %t",
   171  		os.Getpid(), version.String(), race.Enabled, xdebug.Enabled())
   172  
   173  	ctx := context.Background()
   174  	go func() {
   175  		m.afterRunModule(ctx)
   176  	}()
   177  	m.masterStarted.Set(true)
   178  
   179  	reason := "unknown"
   180  	// Block until a signal is received
   181  	select {
   182  	case reason = <-m.chanStoppedByLogic:
   183  	case <-ProcessShutdownNotify():
   184  		reason = fmt.Sprintf("sig(%s)", processShutdownSignal.String())
   185  	}
   186  
   187  	boost.LogInfof("progress closing down by signal, pid: %d, reason: %s", os.Getpid(), reason)
   188  
   189  	m.masterStarted.Set(false)
   190  
   191  	if m.timeoutDuration != 0 {
   192  		var cancelFunc context.CancelFunc
   193  		ctx, cancelFunc = context.WithDeadline(ctx, time.Now().Add(m.timeoutDuration))
   194  		defer cancelFunc()
   195  	}
   196  	beforeCloseModuleDone := make(chan struct{})
   197  	go func() {
   198  		m.beforeCloseModule(ctx)
   199  		close(beforeCloseModuleDone)
   200  	}()
   201  
   202  	select {
   203  	case <-beforeCloseModuleDone:
   204  	case <-ctx.Done():
   205  	}
   206  
   207  	m.closeAll(ctx)
   208  	close(m.chanHasShutdown)
   209  }