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

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
Windows ./gocron.exe web
Windows gocron.exe web
Linux、OSX ./gocron web
```
可选参数

View File

@ -21,6 +21,7 @@ const (
Enabled Status = 1 // 启用
Running Status = 1 // 运行中
Finish Status = 2 // 完成
Cancel Status = 3 // 取消
)
const (

View File

@ -22,6 +22,7 @@ type Task struct {
Protocol TaskProtocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3: 系统命令
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
HostId int16 `xorm:"smallint notnull default 0"` // SSH host id
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) {
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主机名逗号分隔
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
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 '' "` // 执行结果
TotalTime int `xorm:"-"` // 执行总时长
BaseModel `xorm:"-"`
}
func (taskLog *TaskLog) Create() (insertId int64, err error) {
taskLog.Status = Running
_, err = Db.Insert(taskLog)
if err == nil {
insertId = taskLog.Id

View File

@ -14,6 +14,7 @@ var (
AppDir string // 应用根目录
ConfDir string // 配置目录
LogDir string // 日志目录
DataDir string // 存放session等
AppConfig string // 应用配置文件
Installed bool // 应用是否安装过
)
@ -28,8 +29,9 @@ func InitEnv() {
AppDir = wd
ConfDir = AppDir + "/conf"
LogDir = AppDir + "/log"
DataDir = AppDir + "/data"
AppConfig = ConfDir + "/app.ini"
checkDirExists(ConfDir, LogDir)
checkDirExists(ConfDir, LogDir, DataDir)
Installed = IsInstalled()
if Installed {
InitDb()

View File

@ -105,7 +105,10 @@ func RegisterMiddleware(m *macaron.Macaron) {
// 渲染具有缩进格式的 XML默认为不缩进
IndentXML: true,
}))
m.Use(session.Sessioner())
m.Use(session.Sessioner(session.Options{
Provider: "file",
ProviderConfig: app.DataDir + "/sessions",
}))
m.Use(csrf.Csrfer())
m.Use(toolbox.Toolboxer(m))
checkAppInstall(m)

View File

@ -20,6 +20,7 @@ type TaskForm struct {
Protocol models.TaskProtocol `binding:"In(1,2,3)"`
Command string `binding:"Required;MaxSize(512)"`
Timeout int `binding:"Range(0,86400)"`
Multi int8 `binding:"In(1,2)"`
RetryTimes int8
HostId int16
Remark string
@ -119,10 +120,14 @@ func Store(ctx *macaron.Context, form TaskForm) string {
taskModel.Timeout = form.Timeout
taskModel.Remark = form.Remark
taskModel.Status = form.Status
taskModel.Multi = form.Multi
taskModel.RetryTimes = form.RetryTimes
if taskModel.Status != models.Enabled {
taskModel.Status = models.Disabled
}
if taskModel.Multi != 1 {
taskModel.Multi = 0
}
taskModel.Spec = form.Spec
if id == 0 {
id, err = taskModel.Create()

View File

@ -15,6 +15,30 @@ import (
)
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{}
@ -28,6 +52,7 @@ type TaskResult struct {
func (task *Task) Initialize() {
Cron = cron.New()
Cron.Start()
runInstance = Instance{make(map[int]bool)}
taskModel := new(models.Task)
taskList, err := taskModel.ActiveList()
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.TaskId = taskModel.Id
taskLogModel.Name = taskModel.Task.Name
@ -171,7 +196,7 @@ func createTaskLog(taskModel models.TaskHost) (int64, error) {
taskLogModel.Hostname = taskModel.Alias + "-" + taskModel.Name
}
taskLogModel.StartTime = time.Now()
taskLogModel.Status = models.Running
taskLogModel.Status = status
insertId, err := taskLogModel.Create()
return insertId, err
@ -202,7 +227,15 @@ func createJob(taskModel models.TaskHost) cron.FuncJob {
return nil
}
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 {
logger.Error("任务开始执行#写入任务日志失败-", err)
return

View File

@ -63,6 +63,7 @@
<p class="sensorStatus">命令:{{{.Command}}}</p>
<p class="sensorStatus">超时时间:{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</p>
<p>重试次数: {{{.RetryTimes}}}</p>
<p class="sensorStatus">是否允许多实例:{{{if gt .Multi 0}}}是{{{else}}}否{{{end}}}</p>
{{{if eq .Protocol 2}}}
<p>主机: {{{.Alias}}}</p>
{{{end}}}

View File

@ -74,11 +74,13 @@
<td>{{{.RetryTimes}}}</td>
<td>{{{.Hostname}}}</td>
<td>
{{{if ne .Status 3}}}
{{{if gt .TotalTime 0}}}{{{.TotalTime}}}秒{{{else}}}1秒{{{end}}}<br>
开始时间: {{{.StartTime.Format "2006-01-02 15:04:05" }}}<br>
{{{if ne .Status 1}}}
结束时间: {{{.EndTime.Format "2006-01-02 15:04:05" }}}
{{{end}}}
{{{end}}}
</td>
<td>
{{{if eq .Status 2}}}
@ -87,12 +89,12 @@
<span style="color:green">执行中</span>
{{{else if eq .Status 0}}}
<span style="color:red">失败</span>
{{{else}}}
<span style="color:#4499EE">待执行</span>
{{{else if eq .Status 3}}}
<span style="color:#4499EE">取消</span>
{{{end}}}
</td>
<td>
{{{if ne .Status 1}}}
{{{if and (ne .Status 1) (ne .Status 3)}}}
<button class="ui small primary button"
onclick="showResult('{{{.Name}}}', '{{{.Command}}}', '{{{.Result}}}')"
>查看结果

View File

@ -110,6 +110,15 @@
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
</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="field">
<label>任务状态</label>
@ -117,7 +126,7 @@
任务添加成功后,是否立即调度
</div>
<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>
</select>
</div>