新增配置-任务运行是否允许多实例

pull/21/merge
ouqiang 2017-04-26 01:47:38 +08:00
parent a2e4ddf6c5
commit e46a138ac5
11 changed files with 69 additions and 14 deletions

View File

@ -4,7 +4,7 @@
## 安装, 解压后执行 ## 安装, 解压后执行
```shell ```shell
Windows ./gocron.exe web Windows gocron.exe web
Linux、OSX ./gocron web Linux、OSX ./gocron web
``` ```
可选参数 可选参数

View File

@ -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 (

View File

@ -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)
} }
// 更新 // 更新

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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}}}

View File

@ -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}}}')"
>查看结果 >查看结果

View File

@ -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>