2017-03-10 09:24:06 +00:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
2017-09-16 09:58:33 +00:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2018-01-30 11:26:04 +00:00
|
|
|
|
"net/http"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
"github.com/jakecoffman/cron"
|
2018-03-25 05:12:12 +00:00
|
|
|
|
"github.com/ouqiang/gocron/internal/models"
|
|
|
|
|
"github.com/ouqiang/gocron/internal/modules/app"
|
|
|
|
|
"github.com/ouqiang/gocron/internal/modules/httpclient"
|
|
|
|
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
|
|
|
|
"github.com/ouqiang/gocron/internal/modules/notify"
|
|
|
|
|
rpcClient "github.com/ouqiang/gocron/internal/modules/rpc/client"
|
|
|
|
|
pb "github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
2017-03-10 09:24:06 +00:00
|
|
|
|
)
|
|
|
|
|
|
2018-01-27 10:08:46 +00:00
|
|
|
|
var (
|
|
|
|
|
ServiceTask Task
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
// 定时任务调度管理器
|
2018-01-30 11:26:04 +00:00
|
|
|
|
serviceCron *cron.Cron
|
2017-09-16 09:58:33 +00:00
|
|
|
|
|
2018-01-27 10:08:46 +00:00
|
|
|
|
// 同一任务是否有实例处于运行中
|
|
|
|
|
runInstance Instance
|
|
|
|
|
|
|
|
|
|
// 任务计数-正在运行的任务
|
|
|
|
|
taskCount TaskCount
|
2018-01-30 14:36:18 +00:00
|
|
|
|
|
|
|
|
|
// 并发队列, 限制同时运行的任务数量
|
|
|
|
|
concurrencyQueue ConcurrencyQueue
|
2018-01-27 10:08:46 +00:00
|
|
|
|
)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
|
2018-01-30 14:36:18 +00:00
|
|
|
|
// 并发队列
|
|
|
|
|
type ConcurrencyQueue struct {
|
2018-02-04 03:28:48 +00:00
|
|
|
|
queue chan struct{}
|
2018-01-30 14:36:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-04 03:28:48 +00:00
|
|
|
|
func (cq *ConcurrencyQueue) Add() {
|
|
|
|
|
cq.queue <- struct{}{}
|
2018-01-30 14:36:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-04 03:28:48 +00:00
|
|
|
|
func (cq *ConcurrencyQueue) Done() {
|
2018-01-30 14:36:18 +00:00
|
|
|
|
<-cq.queue
|
2018-01-27 10:08:46 +00:00
|
|
|
|
}
|
2017-05-04 10:05:25 +00:00
|
|
|
|
|
|
|
|
|
// 任务计数
|
|
|
|
|
type TaskCount struct {
|
2018-01-30 11:26:04 +00:00
|
|
|
|
wg sync.WaitGroup
|
2018-02-04 03:28:48 +00:00
|
|
|
|
exit chan struct{}
|
2017-05-04 10:05:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 11:26:04 +00:00
|
|
|
|
func (tc *TaskCount) Add() {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
tc.wg.Add(1)
|
2017-05-04 10:05:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 11:26:04 +00:00
|
|
|
|
func (tc *TaskCount) Done() {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
tc.wg.Done()
|
2017-05-04 10:05:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 11:26:04 +00:00
|
|
|
|
func (tc *TaskCount) Exit() {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
tc.wg.Done()
|
|
|
|
|
<-tc.exit
|
|
|
|
|
}
|
2017-05-04 10:05:25 +00:00
|
|
|
|
|
2018-01-30 11:26:04 +00:00
|
|
|
|
func (tc *TaskCount) Wait() {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
tc.Add()
|
|
|
|
|
tc.wg.Wait()
|
|
|
|
|
close(tc.exit)
|
2017-05-04 10:05:25 +00:00
|
|
|
|
}
|
2017-04-25 17:47:38 +00:00
|
|
|
|
|
2017-06-08 10:04:55 +00:00
|
|
|
|
// 任务ID作为Key
|
2017-04-25 17:47:38 +00:00
|
|
|
|
type Instance struct {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
m sync.Map
|
2017-04-25 17:47:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 是否有任务处于运行中
|
|
|
|
|
func (i *Instance) has(key int) bool {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
_, ok := i.m.Load(key)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
|
2018-01-26 14:46:36 +00:00
|
|
|
|
return ok
|
2017-04-25 17:47:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func (i *Instance) add(key int) {
|
2018-02-04 03:28:48 +00:00
|
|
|
|
i.m.Store(key, struct{}{})
|
2017-04-25 17:47:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func (i *Instance) done(key int) {
|
2018-01-26 14:46:36 +00:00
|
|
|
|
i.m.Delete(key)
|
2017-04-25 17:47:38 +00:00
|
|
|
|
}
|
2017-04-13 09:35:59 +00:00
|
|
|
|
|
2017-04-02 02:19:52 +00:00
|
|
|
|
type Task struct{}
|
2017-03-10 09:24:06 +00:00
|
|
|
|
|
2017-04-21 06:50:40 +00:00
|
|
|
|
type TaskResult struct {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
Result string
|
|
|
|
|
Err error
|
|
|
|
|
RetryTimes int8
|
2017-04-21 06:50:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-27 10:08:46 +00:00
|
|
|
|
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
|
|
|
|
func (task Task) Initialize() {
|
2018-01-30 14:36:18 +00:00
|
|
|
|
serviceCron = cron.New()
|
|
|
|
|
serviceCron.Start()
|
2018-02-04 03:28:48 +00:00
|
|
|
|
concurrencyQueue = ConcurrencyQueue{queue: make(chan struct{}, app.Setting.ConcurrencyQueue)}
|
|
|
|
|
taskCount = TaskCount{sync.WaitGroup{}, make(chan struct{})}
|
2018-01-30 14:36:18 +00:00
|
|
|
|
go taskCount.Wait()
|
|
|
|
|
|
2018-01-30 13:16:44 +00:00
|
|
|
|
logger.Info("开始初始化定时任务")
|
2017-09-16 09:58:33 +00:00
|
|
|
|
taskModel := new(models.Task)
|
2018-01-30 13:16:44 +00:00
|
|
|
|
taskNum := 0
|
|
|
|
|
page := 1
|
|
|
|
|
pageSize := 1000
|
|
|
|
|
maxPage := 1000
|
|
|
|
|
for page < maxPage {
|
|
|
|
|
taskList, err := taskModel.ActiveList(page, pageSize)
|
|
|
|
|
if err != nil {
|
2018-05-20 07:24:31 +00:00
|
|
|
|
logger.Fatalf("定时任务初始化#获取任务列表错误: %s", err)
|
2018-01-30 13:16:44 +00:00
|
|
|
|
}
|
|
|
|
|
if len(taskList) == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
for _, item := range taskList {
|
|
|
|
|
task.Add(item)
|
|
|
|
|
taskNum++
|
|
|
|
|
}
|
|
|
|
|
page++
|
2017-09-16 09:58:33 +00:00
|
|
|
|
}
|
2018-01-30 13:16:44 +00:00
|
|
|
|
logger.Infof("定时任务初始化完成, 共%d个定时任务添加到调度器", taskNum)
|
2017-04-20 01:36:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 批量添加任务
|
2018-01-27 10:08:46 +00:00
|
|
|
|
func (task Task) BatchAdd(tasks []models.Task) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
for _, item := range tasks {
|
2018-01-30 13:16:44 +00:00
|
|
|
|
task.RemoveAndAdd(item)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
}
|
2017-03-10 09:24:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 13:16:44 +00:00
|
|
|
|
// 删除任务后添加
|
|
|
|
|
func (task Task) RemoveAndAdd(taskModel models.Task) {
|
|
|
|
|
task.Remove(taskModel.Id)
|
|
|
|
|
task.Add(taskModel)
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-10 09:24:06 +00:00
|
|
|
|
// 添加任务
|
2018-01-27 10:08:46 +00:00
|
|
|
|
func (task Task) Add(taskModel models.Task) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if taskModel.Level == models.TaskLevelChild {
|
|
|
|
|
logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
taskFunc := createJob(taskModel)
|
|
|
|
|
if taskFunc == nil {
|
|
|
|
|
logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cronName := strconv.Itoa(taskModel.Id)
|
2018-01-27 10:08:46 +00:00
|
|
|
|
err := serviceCron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("添加任务到调度器失败#", err)
|
|
|
|
|
}
|
2017-03-10 09:24:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-13 07:42:06 +00:00
|
|
|
|
func (task Task) NextRunTime(taskModel models.Task) time.Time {
|
|
|
|
|
if taskModel.Level != models.TaskLevelParent ||
|
|
|
|
|
taskModel.Status != models.Enabled {
|
|
|
|
|
return time.Time{}
|
|
|
|
|
}
|
|
|
|
|
entries := serviceCron.Entries()
|
|
|
|
|
taskName := strconv.Itoa(taskModel.Id)
|
|
|
|
|
for _, item := range entries {
|
|
|
|
|
if item.Name == taskName {
|
|
|
|
|
return item.Next
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return time.Time{}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-27 03:10:08 +00:00
|
|
|
|
// 停止运行中的任务
|
2018-01-30 11:26:04 +00:00
|
|
|
|
func (task Task) Stop(ip string, port int, id int64) {
|
2018-01-27 03:10:08 +00:00
|
|
|
|
rpcClient.Stop(ip, port, id)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 11:26:04 +00:00
|
|
|
|
func (task Task) Remove(id int) {
|
2018-01-27 10:08:46 +00:00
|
|
|
|
serviceCron.RemoveJob(strconv.Itoa(id))
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-27 03:10:08 +00:00
|
|
|
|
// 等待所有任务结束后退出
|
2018-01-27 10:08:46 +00:00
|
|
|
|
func (task Task) WaitAndExit() {
|
|
|
|
|
serviceCron.Stop()
|
2018-01-26 14:46:36 +00:00
|
|
|
|
taskCount.Exit()
|
2017-05-04 10:05:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-21 05:36:45 +00:00
|
|
|
|
// 直接运行任务
|
2018-01-27 10:08:46 +00:00
|
|
|
|
func (task Task) Run(taskModel models.Task) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
go createJob(taskModel)()
|
2017-04-21 05:36:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-10 09:24:06 +00:00
|
|
|
|
type Handler interface {
|
2018-01-27 03:10:08 +00:00
|
|
|
|
Run(taskModel models.Task, taskUniqueId int64) (string, error)
|
2017-03-10 09:24:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-23 05:31:16 +00:00
|
|
|
|
// HTTP任务
|
2017-04-02 02:19:52 +00:00
|
|
|
|
type HTTPHandler struct{}
|
2017-03-10 09:24:06 +00:00
|
|
|
|
|
2017-05-09 05:42:49 +00:00
|
|
|
|
// http任务执行时间不超过300秒
|
|
|
|
|
const HttpExecTimeout = 300
|
|
|
|
|
|
2018-01-27 03:10:08 +00:00
|
|
|
|
func (h *HTTPHandler) Run(taskModel models.Task, taskUniqueId int64) (result string, err error) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout {
|
|
|
|
|
taskModel.Timeout = HttpExecTimeout
|
|
|
|
|
}
|
2018-01-27 10:08:46 +00:00
|
|
|
|
var resp httpclient.ResponseWrapper
|
2018-01-30 11:26:04 +00:00
|
|
|
|
if taskModel.HttpMethod == models.TaskHTTPMethodGet {
|
2018-01-27 10:08:46 +00:00
|
|
|
|
resp = httpclient.Get(taskModel.Command, taskModel.Timeout)
|
|
|
|
|
} else {
|
|
|
|
|
urlFields := strings.Split(taskModel.Command, "?")
|
|
|
|
|
taskModel.Command = urlFields[0]
|
|
|
|
|
var params string
|
2018-01-30 11:26:04 +00:00
|
|
|
|
if len(urlFields) >= 2 {
|
2018-01-27 10:08:46 +00:00
|
|
|
|
params = urlFields[1]
|
|
|
|
|
}
|
|
|
|
|
resp = httpclient.PostParams(taskModel.Command, params, taskModel.Timeout)
|
|
|
|
|
}
|
2017-09-16 09:58:33 +00:00
|
|
|
|
// 返回状态码非200,均为失败
|
2018-01-27 03:10:08 +00:00
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return resp.Body, err
|
2017-03-10 09:24:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-26 10:09:07 +00:00
|
|
|
|
// RPC调用执行任务
|
2017-09-16 09:58:33 +00:00
|
|
|
|
type RPCHandler struct{}
|
|
|
|
|
|
2018-01-27 03:10:08 +00:00
|
|
|
|
func (h *RPCHandler) Run(taskModel models.Task, taskUniqueId int64) (result string, err error) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
taskRequest := new(pb.TaskRequest)
|
|
|
|
|
taskRequest.Timeout = int32(taskModel.Timeout)
|
|
|
|
|
taskRequest.Command = taskModel.Command
|
2018-01-27 03:10:08 +00:00
|
|
|
|
taskRequest.Id = taskUniqueId
|
2018-01-27 10:08:46 +00:00
|
|
|
|
resultChan := make(chan TaskResult, len(taskModel.Hosts))
|
2017-09-16 09:58:33 +00:00
|
|
|
|
for _, taskHost := range taskModel.Hosts {
|
|
|
|
|
go func(th models.TaskHostDetail) {
|
|
|
|
|
output, err := rpcClient.Exec(th.Name, th.Port, taskRequest)
|
2018-01-27 10:08:46 +00:00
|
|
|
|
errorMessage := ""
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
errorMessage = err.Error()
|
|
|
|
|
}
|
2018-05-06 07:15:27 +00:00
|
|
|
|
outputMessage := fmt.Sprintf("主机: [%s-%s:%d]\n%s\n%s\n\n",
|
|
|
|
|
th.Alias, th.Name, th.Port, errorMessage, output,
|
2017-09-16 09:58:33 +00:00
|
|
|
|
)
|
|
|
|
|
resultChan <- TaskResult{Err: err, Result: outputMessage}
|
|
|
|
|
}(taskHost)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var aggregationErr error = nil
|
2018-01-27 10:08:46 +00:00
|
|
|
|
aggregationResult := ""
|
2017-09-16 09:58:33 +00:00
|
|
|
|
for i := 0; i < len(taskModel.Hosts); i++ {
|
|
|
|
|
taskResult := <-resultChan
|
|
|
|
|
aggregationResult += taskResult.Result
|
|
|
|
|
if taskResult.Err != nil {
|
|
|
|
|
aggregationErr = taskResult.Err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return aggregationResult, aggregationErr
|
2017-03-23 05:31:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-21 06:50:40 +00:00
|
|
|
|
// 创建任务日志
|
2017-08-06 14:49:24 +00:00
|
|
|
|
func createTaskLog(taskModel models.Task, status models.Status) (int64, error) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
taskLogModel := new(models.TaskLog)
|
|
|
|
|
taskLogModel.TaskId = taskModel.Id
|
|
|
|
|
taskLogModel.Name = taskModel.Name
|
|
|
|
|
taskLogModel.Spec = taskModel.Spec
|
|
|
|
|
taskLogModel.Protocol = taskModel.Protocol
|
|
|
|
|
taskLogModel.Command = taskModel.Command
|
|
|
|
|
taskLogModel.Timeout = taskModel.Timeout
|
|
|
|
|
if taskModel.Protocol == models.TaskRPC {
|
2018-01-27 10:08:46 +00:00
|
|
|
|
aggregationHost := ""
|
2017-09-16 09:58:33 +00:00
|
|
|
|
for _, host := range taskModel.Hosts {
|
2018-05-13 07:42:06 +00:00
|
|
|
|
aggregationHost += fmt.Sprintf("%s - %s<br>", host.Alias, host.Name)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
}
|
|
|
|
|
taskLogModel.Hostname = aggregationHost
|
|
|
|
|
}
|
|
|
|
|
taskLogModel.StartTime = time.Now()
|
|
|
|
|
taskLogModel.Status = status
|
|
|
|
|
insertId, err := taskLogModel.Create()
|
|
|
|
|
|
|
|
|
|
return insertId, err
|
2017-03-24 05:06:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-21 06:50:40 +00:00
|
|
|
|
// 更新任务日志
|
|
|
|
|
func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
taskLogModel := new(models.TaskLog)
|
|
|
|
|
var status models.Status
|
2018-01-27 10:08:46 +00:00
|
|
|
|
result := taskResult.Result
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if taskResult.Err != nil {
|
|
|
|
|
status = models.Failure
|
|
|
|
|
} else {
|
|
|
|
|
status = models.Finish
|
|
|
|
|
}
|
|
|
|
|
return taskLogModel.Update(taskLogId, models.CommonMap{
|
|
|
|
|
"retry_times": taskResult.RetryTimes,
|
|
|
|
|
"status": status,
|
|
|
|
|
"result": result,
|
|
|
|
|
})
|
2017-03-23 05:31:16 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-06 14:49:24 +00:00
|
|
|
|
func createJob(taskModel models.Task) cron.FuncJob {
|
2018-01-27 10:08:46 +00:00
|
|
|
|
handler := createHandler(taskModel)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if handler == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
taskFunc := func() {
|
2018-02-04 03:28:48 +00:00
|
|
|
|
taskCount.Add()
|
|
|
|
|
defer taskCount.Done()
|
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
taskLogId := beforeExecJob(taskModel)
|
|
|
|
|
if taskLogId <= 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-01-30 14:36:18 +00:00
|
|
|
|
|
|
|
|
|
if taskModel.Multi == 0 {
|
|
|
|
|
runInstance.add(taskModel.Id)
|
|
|
|
|
defer runInstance.done(taskModel.Id)
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-04 03:28:48 +00:00
|
|
|
|
concurrencyQueue.Add()
|
|
|
|
|
defer concurrencyQueue.Done()
|
2018-01-30 14:36:18 +00:00
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command)
|
2018-01-27 03:10:08 +00:00
|
|
|
|
taskResult := execJob(handler, taskModel, taskLogId)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command)
|
|
|
|
|
afterExecJob(taskModel, taskResult, taskLogId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return taskFunc
|
2017-04-02 02:19:52 +00:00
|
|
|
|
}
|
2017-04-21 06:50:40 +00:00
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func createHandler(taskModel models.Task) Handler {
|
|
|
|
|
var handler Handler = nil
|
|
|
|
|
switch taskModel.Protocol {
|
|
|
|
|
case models.TaskHTTP:
|
|
|
|
|
handler = new(HTTPHandler)
|
|
|
|
|
case models.TaskRPC:
|
|
|
|
|
handler = new(RPCHandler)
|
|
|
|
|
}
|
2017-05-29 09:05:21 +00:00
|
|
|
|
|
2017-09-16 09:58:33 +00:00
|
|
|
|
return handler
|
2017-04-21 06:50:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-08 10:04:55 +00:00
|
|
|
|
// 任务前置操作
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func beforeExecJob(taskModel models.Task) (taskLogId int64) {
|
|
|
|
|
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
|
|
|
|
createTaskLog(taskModel, models.Cancel)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
taskLogId, err := createTaskLog(taskModel, models.Running)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("任务开始执行#写入任务日志失败-", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
logger.Debugf("任务命令-%s", taskModel.Command)
|
|
|
|
|
|
|
|
|
|
return taskLogId
|
2017-05-04 02:47:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-08 10:04:55 +00:00
|
|
|
|
// 任务执行后置操作
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) {
|
|
|
|
|
_, err := updateTaskLog(taskLogId, taskResult)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("任务结束#更新任务日志失败-", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送邮件
|
|
|
|
|
go SendNotification(taskModel, taskResult)
|
|
|
|
|
// 执行依赖任务
|
|
|
|
|
go execDependencyTask(taskModel, taskResult)
|
2017-06-08 10:04:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行依赖任务, 多个任务并发执行
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func execDependencyTask(taskModel models.Task, taskResult TaskResult) {
|
|
|
|
|
// 父任务才能执行子任务
|
|
|
|
|
if taskModel.Level != models.TaskLevelParent {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 是否存在子任务
|
|
|
|
|
dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId)
|
|
|
|
|
if dependencyTaskId == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务
|
|
|
|
|
if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil {
|
|
|
|
|
logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取子任务
|
|
|
|
|
model := new(models.Task)
|
|
|
|
|
tasks, err := model.GetDependencyTaskList(dependencyTaskId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if len(tasks) == 0 {
|
|
|
|
|
logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id)
|
|
|
|
|
}
|
|
|
|
|
for _, task := range tasks {
|
|
|
|
|
task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id)
|
2018-01-27 10:08:46 +00:00
|
|
|
|
ServiceTask.Run(task)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
}
|
2017-05-04 02:47:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送任务结果通知
|
2017-09-16 09:58:33 +00:00
|
|
|
|
func SendNotification(taskModel models.Task, taskResult TaskResult) {
|
|
|
|
|
var statusName string
|
|
|
|
|
// 未开启通知
|
|
|
|
|
if taskModel.NotifyStatus == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-05-13 07:42:06 +00:00
|
|
|
|
if taskModel.NotifyStatus == 3 {
|
|
|
|
|
// 关键字匹配通知
|
|
|
|
|
if !strings.Contains(taskResult.Result, taskModel.NotifyKeyword) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if taskModel.NotifyStatus == 1 && taskResult.Err == nil {
|
|
|
|
|
// 执行失败才发送通知
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-05-13 07:42:06 +00:00
|
|
|
|
if taskModel.NotifyType != 3 && taskModel.NotifyReceiverId == "" {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if taskResult.Err != nil {
|
|
|
|
|
statusName = "失败"
|
|
|
|
|
} else {
|
|
|
|
|
statusName = "成功"
|
|
|
|
|
}
|
|
|
|
|
// 发送通知
|
|
|
|
|
msg := notify.Message{
|
|
|
|
|
"task_type": taskModel.NotifyType,
|
|
|
|
|
"task_receiver_id": taskModel.NotifyReceiverId,
|
|
|
|
|
"name": taskModel.Name,
|
|
|
|
|
"output": taskResult.Result,
|
|
|
|
|
"status": statusName,
|
2018-05-13 07:42:06 +00:00
|
|
|
|
"task_id": taskModel.Id,
|
2017-09-16 09:58:33 +00:00
|
|
|
|
}
|
|
|
|
|
notify.Push(msg)
|
2017-05-04 02:47:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 执行具体任务
|
2018-01-27 03:10:08 +00:00
|
|
|
|
func execJob(handler Handler, taskModel models.Task, taskUniqueId int64) TaskResult {
|
2017-09-16 09:58:33 +00:00
|
|
|
|
defer func() {
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
logger.Error("panic#service/task.go:execJob#", err)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
// 默认只运行任务一次
|
|
|
|
|
var execTimes int8 = 1
|
|
|
|
|
if taskModel.RetryTimes > 0 {
|
|
|
|
|
execTimes += taskModel.RetryTimes
|
|
|
|
|
}
|
|
|
|
|
var i int8 = 0
|
|
|
|
|
var output string
|
|
|
|
|
var err error
|
|
|
|
|
for i < execTimes {
|
2018-01-27 03:10:08 +00:00
|
|
|
|
output, err = handler.Run(taskModel, taskUniqueId)
|
2017-09-16 09:58:33 +00:00
|
|
|
|
if err == nil {
|
|
|
|
|
return TaskResult{Result: output, Err: err, RetryTimes: i}
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
if i < execTimes {
|
|
|
|
|
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
|
2017-12-09 03:01:48 +00:00
|
|
|
|
if taskModel.RetryInterval > 0 {
|
|
|
|
|
time.Sleep(time.Duration(taskModel.RetryInterval) * time.Second)
|
|
|
|
|
} else {
|
|
|
|
|
// 默认重试间隔时间,每次递增1分钟
|
|
|
|
|
time.Sleep(time.Duration(i) * time.Minute)
|
|
|
|
|
}
|
2017-09-16 09:58:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes}
|
|
|
|
|
}
|