gitee.com/h79/goutils@v1.22.10/common/system/daemon.go (about)

     1  package system
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  )
     9  
    10  var (
    11  	DefaultDaemonConfig           = DaemonConfig{Config: Config{EnableEnv: true, EnableStd: false}}
    12  	DefaultGroupRun     *GroupRun = nil
    13  
    14  	CmdNilErr   = errors.New("cmd obj is nil")
    15  	MaxCountErr = errors.New("max count")
    16  	MaxErr      = errors.New("max error")
    17  )
    18  
    19  type DaemonConfig struct {
    20  	Config
    21  	MaxCount    int   `json:"maxCount" yaml:"maxCount" xml:"maxCount"`
    22  	MaxError    int   `json:"maxError" yaml:"maxError" xml:"maxError"`
    23  	MinExitTime int64 `json:"minExitTime" yaml:"minExitTime" xml:"minExitTime"`
    24  }
    25  
    26  type ArgOption struct {
    27  	Id     int
    28  	AppExe string      // 启动的程序
    29  	Args   []string    //参数
    30  	Env    []string    //环境变量
    31  	Data   interface{} // 用户自己定义
    32  }
    33  
    34  type ArgOptionFunc func(arg *ArgOption)
    35  
    36  type Status int
    37  
    38  const (
    39  	KStatusRunning = Status(1)
    40  	KStatusClose   = Status(2)
    41  	KStatusExit    = Status(3)
    42  )
    43  
    44  type ProcessCallback func(cmd *Cmd, status Status, time int64) int
    45  
    46  func callback(cb ProcessCallback, call func(cb ProcessCallback) int) int {
    47  	if cb != nil {
    48  		return call(cb)
    49  	}
    50  	return 0
    51  }
    52  
    53  type Daemon struct {
    54  }
    55  
    56  func (*Daemon) Background(id int, args []string, conf DaemonConfig, opts ...ArgOptionFunc) (*Cmd, error) {
    57  	var _ = &DefaultDaemonConfig
    58  	c, err := NewArgCmd(id, args, &conf.Config, opts...)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	if err = c.Start(); err != nil {
    63  		return nil, err
    64  	}
    65  	return c, nil
    66  }
    67  
    68  // Run 守护进程启动一个子进程, 并循环监视
    69  func (d *Daemon) Run(groupId int, args []string, cb ProcessCallback, config DaemonConfig, opts ...ArgOptionFunc) int {
    70  	var (
    71  		count    = 0
    72  		errNum   = 0
    73  		exitCode = 0
    74  		cmd      *Cmd
    75  		err      error
    76  	)
    77  	d.initConfig(&config)
    78  	for {
    79  		//daemon 信息描述
    80  		if errNum > config.MaxError {
    81  			exitCode = 1
    82  			callback(cb, func(cb ProcessCallback) int {
    83  				return cb(cmd.WithErr(MaxErr), KStatusExit, time.Now().Unix())
    84  			})
    85  			break
    86  		}
    87  		if config.MaxCount > 0 && count > config.MaxCount {
    88  			exitCode = 2
    89  			callback(cb, func(cb ProcessCallback) int {
    90  				return cb(cmd.WithErr(MaxCountErr), KStatusExit, time.Now().Unix())
    91  			})
    92  			break
    93  		}
    94  		//启动时间戳
    95  		startTime := time.Now().Unix()
    96  		cmd, err = d.Background(groupId, args, config, opts...)
    97  		if err != nil { //启动失败
    98  			errNum++
    99  			continue
   100  		}
   101  		// 子进程,
   102  		if cmd == nil || cmd.Cmd == nil {
   103  			callback(cb, func(cb ProcessCallback) int {
   104  				return cb(cmd.WithErr(CmdNilErr), KStatusExit, time.Now().Unix())
   105  			})
   106  			break
   107  		}
   108  		callback(cb, func(cb ProcessCallback) int {
   109  			return cb(cmd, KStatusRunning, startTime)
   110  		})
   111  		count++
   112  		//父进程: 等待子进程退出
   113  		err = cmd.Cmd.Wait()
   114  		end := time.Now().Unix()
   115  		dat := end - startTime
   116  		if dat < config.MinExitTime {
   117  			//异常退出
   118  			errNum++
   119  		} else {
   120  			errNum = 0
   121  		}
   122  		if ret := callback(cb, func(cb ProcessCallback) int {
   123  			return cb(cmd, KStatusClose, end)
   124  		}); ret != 0 {
   125  			exitCode = ret
   126  			break
   127  		}
   128  	}
   129  	return exitCode
   130  }
   131  
   132  const KMaxGroupCount = 3000
   133  
   134  // Group 守护进程启动一个N[1,2000]子进程, 并循环监视
   135  func (*Daemon) Group(group int, args []string, cb ProcessCallback, config DaemonConfig, opts ...ArgOptionFunc) {
   136  	if DefaultGroupRun == nil {
   137  		DefaultGroupRun = NewGroup()
   138  	}
   139  	if group <= 0 {
   140  		group = 1
   141  	}
   142  	if group > KMaxGroupCount {
   143  		group = KMaxGroupCount
   144  	}
   145  	DefaultGroupRun.Exec(cb, config, opts...)
   146  	for i := 0; i < group; i++ {
   147  		_ = DefaultGroupRun.Add(i, NewChild(i, args, nil))
   148  	}
   149  	DefaultGroupRun.Wait()
   150  }
   151  
   152  func (*Daemon) initConfig(conf *DaemonConfig) {
   153  	if conf.MaxError <= 0 {
   154  		conf.MaxError = 10
   155  	}
   156  	if conf.MinExitTime <= 0 {
   157  		conf.MinExitTime = 20
   158  	}
   159  	if conf.MaxError <= 0 {
   160  		conf.MaxError = 5
   161  	}
   162  }
   163  
   164  type Child struct {
   165  	id   int
   166  	args []string
   167  	Data interface{}
   168  }
   169  
   170  func NewChild(id int, args []string, data interface{}) *Child {
   171  	return &Child{
   172  		id:   id,
   173  		args: append([]string{}, args...),
   174  		Data: data,
   175  	}
   176  }
   177  
   178  func (it *Child) GetId() int {
   179  	return it.id
   180  }
   181  
   182  type GroupRun struct {
   183  	wg      sync.WaitGroup
   184  	cc      map[int]*Child
   185  	rm      sync.Mutex
   186  	itCh    chan *Child
   187  	quitCh  chan struct{}
   188  	running RunningCheck
   189  }
   190  
   191  func NewGroup() *GroupRun {
   192  	return &GroupRun{
   193  		cc:     map[int]*Child{},
   194  		itCh:   make(chan *Child),
   195  		quitCh: make(chan struct{}),
   196  	}
   197  }
   198  
   199  func (g *GroupRun) Add(id int, it *Child) error {
   200  	g.rm.Lock()
   201  	_, ok := g.cc[id]
   202  	if ok {
   203  		g.rm.Unlock()
   204  		return fmt.Errorf("%d is existed", id)
   205  	}
   206  	if len(g.cc) > KMaxGroupCount {
   207  		g.rm.Unlock()
   208  		return MaxCountErr
   209  	}
   210  	g.cc[id] = it
   211  	g.rm.Unlock()
   212  
   213  	g.wg.Add(1)
   214  	g.itCh <- it
   215  
   216  	return nil
   217  }
   218  
   219  func (g *GroupRun) Get(id int) (*Child, bool) {
   220  	g.rm.Lock()
   221  	defer g.rm.Unlock()
   222  	it, ok := g.cc[id]
   223  	return it, ok
   224  }
   225  
   226  func (g *GroupRun) Exec(cb ProcessCallback, config DaemonConfig, opts ...ArgOptionFunc) {
   227  	g.running.GoRunning(func() {
   228  		exit := Exit()
   229  		for {
   230  			select {
   231  			case <-exit.Done():
   232  				return
   233  
   234  			case it := <-g.itCh:
   235  				g.run(it, cb, config, opts...)
   236  
   237  			case <-g.quitCh:
   238  				g.quitCh <- struct{}{}
   239  				return
   240  
   241  			case <-Closed():
   242  				return
   243  			}
   244  		}
   245  	})
   246  }
   247  
   248  func (g *GroupRun) Wait() {
   249  	g.wg.Wait()
   250  	// wait execRun runtime quit
   251  	g.quitCh <- struct{}{}
   252  	<-g.quitCh
   253  }
   254  
   255  func (g *GroupRun) run(it *Child, cb ProcessCallback, config DaemonConfig, opts ...ArgOptionFunc) {
   256  	ChildRunning(func() {
   257  		defer func() {
   258  			g.wg.Done()
   259  		}()
   260  		var d = Daemon{}
   261  		d.Run(it.id, it.args, cb, config, opts...)
   262  	})
   263  }