mirror of https://github.com/ouqiang/gocron
feat: 增加定时任务开启、关闭API
parent
b8f13b4b0e
commit
7e010d0aca
|
@ -2,3 +2,7 @@ language: go
|
|||
go:
|
||||
- 1.7.x
|
||||
script: go test `go list ./... | grep -v vendor`
|
||||
|
||||
notifications:
|
||||
on_success: never
|
||||
on_failure: always
|
|
@ -15,5 +15,7 @@ func main() {
|
|||
} else {
|
||||
addr = os.Args[1]
|
||||
}
|
||||
for {
|
||||
server.Start(addr)
|
||||
}
|
||||
}
|
|
@ -30,6 +30,11 @@ func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse,
|
|||
}
|
||||
|
||||
func Start(addr string) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
grpclog.Println("panic", err)
|
||||
}
|
||||
} ()
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
grpclog.Fatal(err)
|
||||
|
|
|
@ -92,6 +92,8 @@ func Register(m *macaron.Macaron) {
|
|||
// API
|
||||
m.Group("/api/v1", func() {
|
||||
m.Post("/tasklog/remove/:id", tasklog.Remove)
|
||||
m.Post("/task/enable/:id", task.Enable)
|
||||
m.Post("/task/disable/:id", task.Disable)
|
||||
}, apiAuth);
|
||||
|
||||
// 404错误
|
||||
|
|
|
@ -53,8 +53,6 @@
|
|||
<option value="2" {{{if .Task}}} {{{if eq .Task.DependencyStatus 2}}}selected{{{end}}} {{{end}}}>弱依赖</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>
|
||||
<div class="content">子任务ID</div>
|
||||
|
@ -108,19 +106,15 @@
|
|||
<textarea rows="5" name="command" placeholder="请输入系统命令" id="command">{{{.Task.Command}}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="six fields">
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>任务超时时间(秒, 0-86400)</label>
|
||||
<input type="text" name="timeout" placeholder="默认0, 不限制" value="{{{if .Task}}} {{{.Task.Timeout}}} {{{else}}} 0 {{{end}}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="six fields">
|
||||
<div class="field">
|
||||
<label>任务失败重试次数 (0-10)</label>
|
||||
<input type="text" name="retry_times" placeholder="默认0, 不重试" value="{{{if .Task}}} {{{.Task.RetryTimes}}} {{{else}}} 0 {{{end}}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>允许多实例同时运行</label>
|
||||
<select name="multi">
|
||||
|
@ -128,6 +122,7 @@
|
|||
<option value="1"{{{if .Task}}} {{{if eq .Task.Multi 1}}}selected{{{end}}} {{{end}}}>是</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 qiang.ou
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,40 +0,0 @@
|
|||
# timewheel
|
||||
Golang实现的时间轮
|
||||
|
||||
|
||||
![时间轮](https://raw.githubusercontent.com/ouqiang/timewheel/master/timewheel.jpg)
|
||||
|
||||
# 安装
|
||||
|
||||
```shell
|
||||
go get -u github.com/ouqiang/timewheel
|
||||
```
|
||||
|
||||
# 使用
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ouqiang/timewheel"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// tick刻度为1秒, 3600个槽
|
||||
tw := timewheel.New(1 * time.Second, 3600)
|
||||
tw.Start()
|
||||
tw.Add(5 * time.Second, func() {
|
||||
// do something
|
||||
})
|
||||
tw.Add(10 * time.Minute, func() {
|
||||
// do something
|
||||
})
|
||||
tw.Add(35 * time.Hour, func() {
|
||||
// do something
|
||||
})
|
||||
// 停止
|
||||
tw.Stop()
|
||||
}
|
||||
```
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
package timewheel
|
||||
|
||||
import (
|
||||
"time"
|
||||
"container/list"
|
||||
)
|
||||
|
||||
// @author qiang.ou<qingqianludao@gmail.com>
|
||||
|
||||
type Job func([]interface{})
|
||||
|
||||
type TimeWheel struct {
|
||||
interval time.Duration
|
||||
ticker *time.Ticker
|
||||
slots []*list.List
|
||||
currentPos int
|
||||
slotNum int
|
||||
job Job
|
||||
taskChannel chan Task
|
||||
stopChannel chan bool
|
||||
}
|
||||
|
||||
|
||||
type Task struct {
|
||||
delay time.Duration
|
||||
circle int
|
||||
data []interface{}
|
||||
}
|
||||
|
||||
func New(interval time.Duration, slotNum int, job Job) *TimeWheel {
|
||||
if interval <= 0 || slotNum <= 0 || job == nil {
|
||||
return nil
|
||||
}
|
||||
tw := &TimeWheel{
|
||||
interval: interval,
|
||||
slots: make([]*list.List, slotNum),
|
||||
currentPos: 0,
|
||||
job: job,
|
||||
slotNum: slotNum,
|
||||
taskChannel: make(chan Task),
|
||||
stopChannel: make(chan bool),
|
||||
}
|
||||
|
||||
tw.initSlots()
|
||||
|
||||
return tw
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) initSlots() {
|
||||
for i := 0; i < tw.slotNum; i++ {
|
||||
tw.slots[i] = list.New()
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) Start() {
|
||||
tw.ticker = time.NewTicker(tw.interval)
|
||||
go tw.start()
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) Add(delay time.Duration, data []interface{}) {
|
||||
if delay <= 0 {
|
||||
return
|
||||
}
|
||||
tw.taskChannel <- Task{delay:delay, data: data}
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) Stop() {
|
||||
tw.stopChannel <- true
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) start() {
|
||||
for {
|
||||
select {
|
||||
case <- tw.ticker.C:
|
||||
tw.tickHandler()
|
||||
case task := <- tw.taskChannel:
|
||||
tw.addTask(&task)
|
||||
case <- tw.stopChannel:
|
||||
tw.ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) tickHandler() {
|
||||
l := tw.slots[tw.currentPos]
|
||||
tw.scanAndRunTask(l)
|
||||
if tw.currentPos == tw.slotNum - 1 {
|
||||
tw.currentPos = 0
|
||||
} else {
|
||||
tw.currentPos++
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) scanAndRunTask(l *list.List) {
|
||||
for e := l.Front(); e != nil; {
|
||||
task := e.Value.(*Task)
|
||||
if task.circle > 0 {
|
||||
task.circle--
|
||||
e = e.Next()
|
||||
continue
|
||||
}
|
||||
|
||||
go tw.job(task.data)
|
||||
next := e.Next()
|
||||
l.Remove(e)
|
||||
e = next
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) addTask(task *Task) {
|
||||
pos, circle := tw.getPositionAndCircle(task.delay)
|
||||
task.circle = circle
|
||||
|
||||
tw.slots[pos].PushBack(task)
|
||||
}
|
||||
|
||||
func (tw *TimeWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) {
|
||||
delaySeconds := int(d.Seconds())
|
||||
intervalSeconds := int(tw.interval.Seconds())
|
||||
circle = int(delaySeconds / intervalSeconds / tw.slotNum)
|
||||
pos = int(tw.currentPos + delaySeconds / intervalSeconds) % tw.slotNum
|
||||
|
||||
|
||||
return
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
|
@ -142,12 +142,6 @@
|
|||
"revision": "09cded8978dc9e80714c4d85b0322337b0a1e5e0",
|
||||
"revisionTime": "2016-03-02T07:53:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kIFW+u9fHefC8sWE4W9pYIfJv5k=",
|
||||
"path": "github.com/ouqiang/timewheel",
|
||||
"revision": "c28ec761087c32fd75ad7514db2a4988d5c872d9",
|
||||
"revisionTime": "2017-05-14T12:16:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "cVGA2CJTJsCAVa5VKTM8k/ma/BU=",
|
||||
"path": "github.com/silenceper/pool",
|
||||
|
|
Loading…
Reference in New Issue