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 }