github.com/keysonzzz/kmg@v0.0.0-20151121023212-05317bfd7d39/kmgScheduleTask/Task.go.bak (about) 1 package kmgScheduleTask 2 3 import ( 4 "fmt" 5 "github.com/bronze1man/kmg/encoding/kmgJson" 6 "github.com/bronze1man/kmg/kmgSql" 7 "github.com/bronze1man/kmg/kmgTime" 8 "reflect" 9 "runtime" 10 "strconv" 11 "sync" 12 "time" 13 ) 14 15 type Status string 16 17 const ( 18 MySQLTableName string = "KmgScheduleTask" 19 DefaultMaxAllowExecuteTime time.Duration = 10 * time.Minute 20 DefaultMaxAllowNumberOfRetry int = 3 21 ) 22 23 type Task struct { 24 Id int 25 ExecuteTime time.Time // 计划执行的时间点 26 Func func(isDone chan bool, task Task) // Task 要执行的函数 27 Parameter map[string]string // Task 的参数 28 IsDone bool // Task 是否已经已经完成 29 MaxAllowExecuteSecond time.Duration 30 MaxAllowNumberOfRetry int // Task 允许的最大重试次数 31 NumberOfTry int // Task 已重试的次数 TODO 暂时不在数据库保存该值 32 } 33 34 func (task *Task) doneAndPersist() { 35 row := map[string]string{} 36 if task.Id != 0 { 37 row["Id"] = strconv.Itoa(task.Id) 38 } 39 row["ExecuteTime"] = kmgTime.DefaultFormat(task.ExecuteTime) 40 row["Func"] = getFuncFullName(task.Func) 41 row["Parameter"] = kmgJson.MustMarshalToString(task.Parameter) 42 row["IsDone"] = "1" 43 row["MaxAllowExecuteSecond"] = task.MaxAllowExecuteSecond.String() 44 row["MaxAllowNumberOfRetry"] = strconv.Itoa(task.MaxAllowNumberOfRetry) 45 kmgSql.MustReplaceById(MySQLTableName, "Id", row) 46 } 47 48 var taskSlice []*Task 49 50 var taskFuncMap map[string]func(isDone chan bool, task Task) = map[string]func(isDone chan bool, task Task){} 51 52 func getFuncFromString(fullName string) func(isDone chan bool, task Task) { 53 function, ok := taskFuncMap[fullName] 54 if ok { 55 return function 56 } 57 return nil 58 } 59 60 func getFuncFullName(function interface{}) string { 61 v := reflect.ValueOf(function) 62 f := runtime.FuncForPC(v.Pointer()) 63 return f.Name() 64 } 65 66 func recoverFromDb() { 67 rowList := kmgSql.MustQuery("SELECT * FROM `"+MySQLTableName+"` WHERE IsDone<>?", "1") 68 for _, row := range rowList { 69 task := &Task{ 70 ExecuteTime: kmgTime.MustFromMysqlFormatDefaultTZ(row["ExecuteTime"]), 71 Func: getFuncFromString(row["Func"]), 72 IsDone: false, 73 } 74 task.Parameter = make(map[string]string) 75 kmgJson.MustUnmarshal([]byte(row["Parameter"]), task.Parameter) 76 id, err := strconv.Atoi(row["Id"]) 77 if err != nil { 78 id = 0 79 log(task, err.Error()) 80 } 81 task.Id = id 82 maxAllowExecuteSecond, err := strconv.Atoi(row["MaxAllowExecuteSecond"]) 83 if err != nil { 84 maxAllowExecuteSecond = 0 85 log(task, err.Error()) 86 } 87 task.MaxAllowExecuteSecond = time.Duration(maxAllowExecuteSecond) 88 maxAllowNumberOfRetry, err := strconv.Atoi(row["MaxAllowNumberOfRetry"]) 89 if err != nil { 90 maxAllowNumberOfRetry = 0 91 log(task, err.Error()) 92 } 93 task.MaxAllowNumberOfRetry = maxAllowNumberOfRetry 94 taskSlice = append(taskSlice, task) 95 } 96 } 97 98 func run() { 99 for _, task := range taskSlice { 100 if task.IsDone { 101 //TODO 将其从 taskSlice 中删除,提高一点点性能 102 continue 103 } 104 now := kmgTime.NowTime.Now() 105 d := now.Sub(task.ExecuteTime) 106 if int(d) < 0 { 107 continue 108 } 109 if task.Func == nil { 110 log(task, "task has no Func to execute") 111 task.IsDone = true // TODO 以后都不再理它,避免打一堆没用的Log 112 continue 113 } 114 exec(task) 115 } 116 time.Sleep(time.Second) 117 run() 118 } 119 120 func exec(task *Task) { 121 maxAllowNumberOfRetry := DefaultMaxAllowNumberOfRetry 122 if task.MaxAllowNumberOfRetry > 0 { 123 maxAllowNumberOfRetry = task.MaxAllowNumberOfRetry 124 } 125 if task.NumberOfTry >= maxAllowNumberOfRetry { //TODO 似乎这里不需要判断 126 return 127 } 128 task.NumberOfTry++ 129 timeout := DefaultMaxAllowExecuteTime 130 if task.MaxAllowExecuteSecond > 0 { 131 timeout = task.MaxAllowExecuteSecond * time.Second 132 } 133 isDoneCh := make(chan bool) 134 go task.Func(isDoneCh, *task) 135 select { 136 case isDone := <-isDoneCh: 137 fmt.Println(isDone) 138 if isDone { 139 task.doneAndPersist() 140 return 141 } 142 if task.NumberOfTry >= maxAllowNumberOfRetry { 143 task.doneAndPersist() 144 log(task, "task failed "+strconv.Itoa(task.NumberOfTry)+" times") 145 return 146 } 147 exec(task) 148 case <-time.After(timeout): 149 task.doneAndPersist() 150 log(task, "time out") 151 } 152 } 153 154 func log(task *Task, info string) { 155 fmt.Println(info) 156 } 157 158 var initOnce sync.Once 159 160 func Start() { 161 initOnce.Do(func() { 162 kmgSql.MustRegisterTable(kmgSql.Table{ 163 Name: MySQLTableName, 164 FieldList: map[string]kmgSql.DbType{ 165 "Id": kmgSql.DbTypeIntAutoIncrement, 166 "ExecuteTime": kmgSql.DbTypeDatetime, 167 "Func": kmgSql.DbTypeString, 168 "Parameter": kmgSql.DbTypeLongBlob, 169 "IsDone": kmgSql.DbTypeBool, 170 "MaxAllowExecuteSecond": kmgSql.DbTypeString, 171 "MaxAllowNumberOfRetry": kmgSql.DbTypeInt, 172 }, 173 PrimaryKey: "Id", 174 }) 175 kmgSql.SyncDbCommand() 176 recoverFromDb() 177 run() 178 }) 179 } 180 181 //可以在任何位置注册 182 func RegisterTask(task Task) { 183 taskSlice = append(taskSlice, &task) 184 } 185 186 //必须在 Start 前注册好一些数据库中可能保存的 TaskFunc 187 func RegisterTaskFunc(taskFunc interface{}) { 188 v := reflect.ValueOf(taskFunc) 189 f := runtime.FuncForPC(v.Pointer()) 190 taskFuncMap[f.Name()] = v.Interface().(func(isDone chan bool, task Task)) 191 }