新增配置-任务运行是否允许多实例
parent
a2e4ddf6c5
commit
e46a138ac5
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
## 安装, 解压后执行
|
## 安装, 解压后执行
|
||||||
```shell
|
```shell
|
||||||
Windows ./gocron.exe web
|
Windows gocron.exe web
|
||||||
Linux、OSX ./gocron web
|
Linux、OSX ./gocron web
|
||||||
```
|
```
|
||||||
可选参数
|
可选参数
|
||||||
|
|
|
@ -21,6 +21,7 @@ const (
|
||||||
Enabled Status = 1 // 启用
|
Enabled Status = 1 // 启用
|
||||||
Running Status = 1 // 运行中
|
Running Status = 1 // 运行中
|
||||||
Finish Status = 2 // 完成
|
Finish Status = 2 // 完成
|
||||||
|
Cancel Status = 3 // 取消
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -22,6 +22,7 @@ type Task struct {
|
||||||
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3: 系统命令
|
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3: 系统命令
|
||||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||||
|
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
||||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
||||||
HostId int16 `xorm:"smallint notnull default 0"` // SSH host id,
|
HostId int16 `xorm:"smallint notnull default 0"` // SSH host id,
|
||||||
Remark string `xorm:"varchar(100) notnull default ''"` // 备注
|
Remark string `xorm:"varchar(100) notnull default ''"` // 备注
|
||||||
|
@ -57,7 +58,7 @@ func (task *Task) Create() (insertId int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (task *Task) UpdateBean(id int) (int64, error) {
|
func (task *Task) UpdateBean(id int) (int64, error) {
|
||||||
return Db.ID(id).UseBool("status").Update(task)
|
return Db.ID(id).UseBool("status,multi").Update(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新
|
// 更新
|
||||||
|
|
|
@ -21,15 +21,13 @@ type TaskLog struct {
|
||||||
Hostname string `xorm:"varchar(128) notnull defalut '' "` // SSH主机名,逗号分隔
|
Hostname string `xorm:"varchar(128) notnull defalut '' "` // SSH主机名,逗号分隔
|
||||||
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
||||||
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
||||||
Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕
|
Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成)
|
||||||
Result string `xorm:"text notnull defalut '' "` // 执行结果
|
Result string `xorm:"text notnull defalut '' "` // 执行结果
|
||||||
TotalTime int `xorm:"-"` // 执行总时长
|
TotalTime int `xorm:"-"` // 执行总时长
|
||||||
BaseModel `xorm:"-"`
|
BaseModel `xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (taskLog *TaskLog) Create() (insertId int64, err error) {
|
func (taskLog *TaskLog) Create() (insertId int64, err error) {
|
||||||
taskLog.Status = Running
|
|
||||||
|
|
||||||
_, err = Db.Insert(taskLog)
|
_, err = Db.Insert(taskLog)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
insertId = taskLog.Id
|
insertId = taskLog.Id
|
||||||
|
|
|
@ -14,6 +14,7 @@ var (
|
||||||
AppDir string // 应用根目录
|
AppDir string // 应用根目录
|
||||||
ConfDir string // 配置目录
|
ConfDir string // 配置目录
|
||||||
LogDir string // 日志目录
|
LogDir string // 日志目录
|
||||||
|
DataDir string // 存放session等
|
||||||
AppConfig string // 应用配置文件
|
AppConfig string // 应用配置文件
|
||||||
Installed bool // 应用是否安装过
|
Installed bool // 应用是否安装过
|
||||||
)
|
)
|
||||||
|
@ -28,8 +29,9 @@ func InitEnv() {
|
||||||
AppDir = wd
|
AppDir = wd
|
||||||
ConfDir = AppDir + "/conf"
|
ConfDir = AppDir + "/conf"
|
||||||
LogDir = AppDir + "/log"
|
LogDir = AppDir + "/log"
|
||||||
|
DataDir = AppDir + "/data"
|
||||||
AppConfig = ConfDir + "/app.ini"
|
AppConfig = ConfDir + "/app.ini"
|
||||||
checkDirExists(ConfDir, LogDir)
|
checkDirExists(ConfDir, LogDir, DataDir)
|
||||||
Installed = IsInstalled()
|
Installed = IsInstalled()
|
||||||
if Installed {
|
if Installed {
|
||||||
InitDb()
|
InitDb()
|
||||||
|
|
|
@ -105,7 +105,10 @@ func RegisterMiddleware(m *macaron.Macaron) {
|
||||||
// 渲染具有缩进格式的 XML,默认为不缩进
|
// 渲染具有缩进格式的 XML,默认为不缩进
|
||||||
IndentXML: true,
|
IndentXML: true,
|
||||||
}))
|
}))
|
||||||
m.Use(session.Sessioner())
|
m.Use(session.Sessioner(session.Options{
|
||||||
|
Provider: "file",
|
||||||
|
ProviderConfig: app.DataDir + "/sessions",
|
||||||
|
}))
|
||||||
m.Use(csrf.Csrfer())
|
m.Use(csrf.Csrfer())
|
||||||
m.Use(toolbox.Toolboxer(m))
|
m.Use(toolbox.Toolboxer(m))
|
||||||
checkAppInstall(m)
|
checkAppInstall(m)
|
||||||
|
|
|
@ -20,6 +20,7 @@ type TaskForm struct {
|
||||||
Protocol models.TaskProtocol `binding:"In(1,2,3)"`
|
Protocol models.TaskProtocol `binding:"In(1,2,3)"`
|
||||||
Command string `binding:"Required;MaxSize(512)"`
|
Command string `binding:"Required;MaxSize(512)"`
|
||||||
Timeout int `binding:"Range(0,86400)"`
|
Timeout int `binding:"Range(0,86400)"`
|
||||||
|
Multi int8 `binding:"In(1,2)"`
|
||||||
RetryTimes int8
|
RetryTimes int8
|
||||||
HostId int16
|
HostId int16
|
||||||
Remark string
|
Remark string
|
||||||
|
@ -119,10 +120,14 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
taskModel.Timeout = form.Timeout
|
taskModel.Timeout = form.Timeout
|
||||||
taskModel.Remark = form.Remark
|
taskModel.Remark = form.Remark
|
||||||
taskModel.Status = form.Status
|
taskModel.Status = form.Status
|
||||||
|
taskModel.Multi = form.Multi
|
||||||
taskModel.RetryTimes = form.RetryTimes
|
taskModel.RetryTimes = form.RetryTimes
|
||||||
if taskModel.Status != models.Enabled {
|
if taskModel.Status != models.Enabled {
|
||||||
taskModel.Status = models.Disabled
|
taskModel.Status = models.Disabled
|
||||||
}
|
}
|
||||||
|
if taskModel.Multi != 1 {
|
||||||
|
taskModel.Multi = 0
|
||||||
|
}
|
||||||
taskModel.Spec = form.Spec
|
taskModel.Spec = form.Spec
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
id, err = taskModel.Create()
|
id, err = taskModel.Create()
|
||||||
|
|
|
@ -15,6 +15,30 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var Cron *cron.Cron
|
var Cron *cron.Cron
|
||||||
|
var runInstance Instance
|
||||||
|
|
||||||
|
// 任务ID作为Key, 不会出现并发读写, 不加锁
|
||||||
|
type Instance struct {
|
||||||
|
Status map[int]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否有任务处于运行中
|
||||||
|
func (i *Instance) has(key int) bool {
|
||||||
|
running, ok := i.Status[key]
|
||||||
|
if ok && running {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) add(key int) {
|
||||||
|
i.Status[key] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) done(key int) {
|
||||||
|
i.Status[key] = false
|
||||||
|
}
|
||||||
|
|
||||||
type Task struct{}
|
type Task struct{}
|
||||||
|
|
||||||
|
@ -28,6 +52,7 @@ type TaskResult struct {
|
||||||
func (task *Task) Initialize() {
|
func (task *Task) Initialize() {
|
||||||
Cron = cron.New()
|
Cron = cron.New()
|
||||||
Cron.Start()
|
Cron.Start()
|
||||||
|
runInstance = Instance{make(map[int]bool)}
|
||||||
taskModel := new(models.Task)
|
taskModel := new(models.Task)
|
||||||
taskList, err := taskModel.ActiveList()
|
taskList, err := taskModel.ActiveList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -159,7 +184,7 @@ func (h *SSHCommandHandler) Run(taskModel models.TaskHost) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建任务日志
|
// 创建任务日志
|
||||||
func createTaskLog(taskModel models.TaskHost) (int64, error) {
|
func createTaskLog(taskModel models.TaskHost, status models.Status) (int64, error) {
|
||||||
taskLogModel := new(models.TaskLog)
|
taskLogModel := new(models.TaskLog)
|
||||||
taskLogModel.TaskId = taskModel.Id
|
taskLogModel.TaskId = taskModel.Id
|
||||||
taskLogModel.Name = taskModel.Task.Name
|
taskLogModel.Name = taskModel.Task.Name
|
||||||
|
@ -171,7 +196,7 @@ func createTaskLog(taskModel models.TaskHost) (int64, error) {
|
||||||
taskLogModel.Hostname = taskModel.Alias + "-" + taskModel.Name
|
taskLogModel.Hostname = taskModel.Alias + "-" + taskModel.Name
|
||||||
}
|
}
|
||||||
taskLogModel.StartTime = time.Now()
|
taskLogModel.StartTime = time.Now()
|
||||||
taskLogModel.Status = models.Running
|
taskLogModel.Status = status
|
||||||
insertId, err := taskLogModel.Create()
|
insertId, err := taskLogModel.Create()
|
||||||
|
|
||||||
return insertId, err
|
return insertId, err
|
||||||
|
@ -202,7 +227,15 @@ func createJob(taskModel models.TaskHost) cron.FuncJob {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
taskFunc := func() {
|
taskFunc := func() {
|
||||||
taskLogId, err := createTaskLog(taskModel)
|
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
||||||
|
createTaskLog(taskModel, models.Cancel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if taskModel.Multi == 0 {
|
||||||
|
runInstance.add(taskModel.Id)
|
||||||
|
defer runInstance.done(taskModel.Id)
|
||||||
|
}
|
||||||
|
taskLogId, err := createTaskLog(taskModel, models.Running)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("任务开始执行#写入任务日志失败-", err)
|
logger.Error("任务开始执行#写入任务日志失败-", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
<p class="sensorStatus">命令:{{{.Command}}}</p>
|
<p class="sensorStatus">命令:{{{.Command}}}</p>
|
||||||
<p class="sensorStatus">超时时间:{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</p>
|
<p class="sensorStatus">超时时间:{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</p>
|
||||||
<p>重试次数: {{{.RetryTimes}}}</p>
|
<p>重试次数: {{{.RetryTimes}}}</p>
|
||||||
|
<p class="sensorStatus">是否允许多实例:{{{if gt .Multi 0}}}是{{{else}}}否{{{end}}}</p>
|
||||||
{{{if eq .Protocol 2}}}
|
{{{if eq .Protocol 2}}}
|
||||||
<p>主机: {{{.Alias}}}</p>
|
<p>主机: {{{.Alias}}}</p>
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
|
|
|
@ -74,11 +74,13 @@
|
||||||
<td>{{{.RetryTimes}}}</td>
|
<td>{{{.RetryTimes}}}</td>
|
||||||
<td>{{{.Hostname}}}</td>
|
<td>{{{.Hostname}}}</td>
|
||||||
<td>
|
<td>
|
||||||
|
{{{if ne .Status 3}}}
|
||||||
{{{if gt .TotalTime 0}}}{{{.TotalTime}}}秒{{{else}}}1秒{{{end}}}<br>
|
{{{if gt .TotalTime 0}}}{{{.TotalTime}}}秒{{{else}}}1秒{{{end}}}<br>
|
||||||
开始时间: {{{.StartTime.Format "2006-01-02 15:04:05" }}}<br>
|
开始时间: {{{.StartTime.Format "2006-01-02 15:04:05" }}}<br>
|
||||||
{{{if ne .Status 1}}}
|
{{{if ne .Status 1}}}
|
||||||
结束时间: {{{.EndTime.Format "2006-01-02 15:04:05" }}}
|
结束时间: {{{.EndTime.Format "2006-01-02 15:04:05" }}}
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
|
{{{end}}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{{if eq .Status 2}}}
|
{{{if eq .Status 2}}}
|
||||||
|
@ -87,12 +89,12 @@
|
||||||
<span style="color:green">执行中</span>
|
<span style="color:green">执行中</span>
|
||||||
{{{else if eq .Status 0}}}
|
{{{else if eq .Status 0}}}
|
||||||
<span style="color:red">失败</span>
|
<span style="color:red">失败</span>
|
||||||
{{{else}}}
|
{{{else if eq .Status 3}}}
|
||||||
<span style="color:#4499EE">待执行</span>
|
<span style="color:#4499EE">取消</span>
|
||||||
{{{end}}}
|
{{{end}}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{{if ne .Status 1}}}
|
{{{if and (ne .Status 1) (ne .Status 3)}}}
|
||||||
<button class="ui small primary button"
|
<button class="ui small primary button"
|
||||||
onclick="showResult('{{{.Name}}}', '{{{.Command}}}', '{{{.Result}}}')"
|
onclick="showResult('{{{.Name}}}', '{{{.Command}}}', '{{{.Result}}}')"
|
||||||
>查看结果
|
>查看结果
|
||||||
|
|
|
@ -110,6 +110,15 @@
|
||||||
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
|
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="three fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>允许多实例运行</label>
|
||||||
|
<select name="multi">
|
||||||
|
<option value="1"{{{if .Task}}} {{{if eq .Task.Multi 1}}}selected{{{end}}} {{{end}}}>是</option>
|
||||||
|
<option value="2" {{{if .Task}}} {{{if eq .Task.Multi 0}}}selected{{{end}}} {{{end}}}>否</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="three fields">
|
<div class="three fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>任务状态</label>
|
<label>任务状态</label>
|
||||||
|
@ -117,7 +126,7 @@
|
||||||
任务添加成功后,是否立即调度
|
任务添加成功后,是否立即调度
|
||||||
</div>
|
</div>
|
||||||
<select name="status">
|
<select name="status">
|
||||||
<option value="2"{{{if .Task}}} {{{if eq .Task.Status 2}}}selected{{{end}}} {{{end}}}>暂停</option>
|
<option value="2"{{{if .Task}}} {{{if eq .Task.Status 0}}}selected{{{end}}} {{{end}}}>暂停</option>
|
||||||
<option value="1" {{{if .Task}}} {{{if eq .Task.Status 1}}}selected{{{end}}} {{{end}}}>激活</option>
|
<option value="1" {{{if .Task}}} {{{if eq .Task.Status 1}}}selected{{{end}}} {{{end}}}>激活</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue