一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - Go定時器cron的使用詳解

Go定時器cron的使用詳解

2020-05-13 11:07騎頭豬逛街 Golang

本篇文章主要介紹了Go定時器cron的使用詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

cron是什么

cron的意思就是:計劃任務,說白了就是定時任務。我和系統約個時間,你在幾點幾分幾秒或者每隔幾分鐘跑一個任務(job),就那么簡單。

cron表達式  

cron表達式是一個好東西,這個東西不僅Java的quartZ能用到,Go語言中也可以用到。我沒有用過Linux的cron,但網上說Linux也是可以用crontab -e 命令來配置定時任務。Go語言和Java中都是可以精確到秒的,但是Linux中不行。

cron表達式代表一個時間的集合,使用6個空格分隔的字段表示:

 

字段名

是否必須

允許的值 

允許的特定字符

秒(Seconds)

0-59

* / , -

分(Minute)

0-59

* / , -

時(Hours)

0-23

* / , -

日(Day of month)

1-31

* / , - ?

月(Month)

1-12 或 JAN-DEC

* / , -

星期(Day of week)

0-6 或 SUM-SAT

* / , - ?

 

1.月(Month)和星期(Day of week)字段的值不區分大小寫,如:SUN、Sun 和 sun 是一樣的。

2.星期(Day of week)字段如果沒提供,相當于是 *

?
1
2
3
4
5
6
7
8
9
# ┌───────────── min (0 - 59)
# │ ┌────────────── hour (0 - 23)
# │ │ ┌─────────────── day of month (1 - 31)
# │ │ │ ┌──────────────── month (1 - 12)
# │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to
# │ │ │ │ │         Saturday, or use names; 7 is also Sunday)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * command to execute

cron特定字符說明

1)星號(*)

表示 cron 表達式能匹配該字段的所有值。如在第5個字段使用星號(month),表示每個月

2)斜線(/)

表示增長間隔,如第1個字段(minutes) 值是 3-59/15,表示每小時的第3分鐘開始執行一次,之后每隔 15 分鐘執行一次(即 3、18、33、48 這些時間點執行),這里也可以表示為:3/15

3)逗號(,)

用于枚舉值,如第6個字段值是 MON,WED,FRI,表示 星期一、三、五 執行

4)連字號(-)

表示一個范圍,如第3個字段的值為 9-17 表示 9am 到 5pm 直接每個小時(包括9和17)

5)問號(?)

只用于 日(Day of month) 和 星期(Day of week),表示不指定值,可以用于代替 *

6)L,W,#

Go中沒有L,W,#的用法,下文作解釋。

cron舉例說明

每隔5秒執行一次:*/5 * * * * ?

每隔1分鐘執行一次:0 */1 * * * ?

每天23點執行一次:0 0 23 * * ?

每天凌晨1點執行一次:0 0 1 * * ?

每月1號凌晨1點執行一次:0 0 1 1 * ?

在26分、29分、33分執行一次:0 26,29,33 * * * ?

每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ?

下載安裝

控制臺輸入 go get github.com/robfig/cron 去下載定時任務的Go包,前提是你的 $GOPATH 已經配置好

源碼解析

文件目錄講解 

?
1
2
3
4
5
6
7
8
9
10
11
constantdelay.go   #一個最簡單的秒級別定時系統。與cron無關
constantdelay_test.go #測試
cron.go        #Cron系統。管理一系列的cron定時任務(Schedule Job)
cron_test.go     #測試
doc.go        #說明文檔
LICENSE        #授權書
parser.go       #解析器,解析cron格式字符串城一個具體的定時器(Schedule)
parser_test.go    #測試
README.md       #README
spec.go        #單個定時器(Schedule)結構體。如何計算自己的下一次觸發時間
spec_test.go     #測試

cron.go

結構體:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
// Cron保持任意數量的條目的軌道,調用相關的func時間表指定。它可以被啟動,停止和條目,可運行的同時進行檢查。
type Cron struct {
  entries []*Entry      // 任務
  stop   chan struct{}   // 叫停止的途徑
  add   chan *Entry    // 添加新任務的方式
  snapshot chan []*Entry   // 請求獲取任務快照的方式
  running bool        // 是否在運行
  ErrorLog *log.Logger    // 出錯日志(新增屬性)
  location *time.Location   // 所在地區(新增屬性)   
}
?
1
2
3
4
5
6
7
8
9
10
11
12
// Entry consists of a schedule and the func to execute on that schedule.
// 入口包括時間表和可在時間表上執行的func
type Entry struct {
    // 計時器
  Schedule Schedule
  // 下次執行時間
  Next time.Time
  // 上次執行時間
  Prev time.Time
  // 任務
  Job Job
}

關鍵方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 開始任務
// Start the cron scheduler in its own go-routine, or no-op if already started.
func (c *Cron) Start() {
  if c.running {
    return
  }
  c.running = true
  go c.run()
}
// 結束任務
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
func (c *Cron) Stop() {
  if !c.running {
    return
  }
  c.stop <- struct{}{}
  c.running = false
}
 
// 執行定時任務
// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
  // Figure out the next activation times for each entry.
  now := time.Now().In(c.location)
  for _, entry := range c.entries {
    entry.Next = entry.Schedule.Next(now)
  }
    // 無限循環
  for {
      //通過對下一個執行時間進行排序,判斷那些任務是下一次被執行的,防在隊列的前面.sort是用來做排序的
    sort.Sort(byTime(c.entries))
 
    var effective time.Time
    if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
      // If there are no entries yet, just sleep - it still handles new entries
      // and stop requests.
      effective = now.AddDate(10, 0, 0)
    } else {
      effective = c.entries[0].Next
    }
 
    timer := time.NewTimer(effective.Sub(now))
    select {
    case now = <-timer.C: // 執行當前任務
      now = now.In(c.location)
      // Run every entry whose next time was this effective time.
      for _, e := range c.entries {
        if e.Next != effective {
          break
        }
        go c.runWithRecovery(e.Job)
        e.Prev = e.Next
        e.Next = e.Schedule.Next(now)
      }
      continue
 
    case newEntry := <-c.add: // 添加新的任務
      c.entries = append(c.entries, newEntry)
      newEntry.Next = newEntry.Schedule.Next(time.Now().In(c.location))
 
    case <-c.snapshot: // 獲取快照
      c.snapshot <- c.entrySnapshot()
 
    case <-c.stop:  // 停止任務
      timer.Stop()
      return
    }
 
    // 'now' should be updated after newEntry and snapshot cases.
    now = time.Now().In(c.location)
    timer.Stop()
  }
}

spec.go

結構體及關鍵方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
// traditional crontab specification. It is computed initially and stored as bit sets.
type SpecSchedule struct {
  // 表達式中鎖表明的,秒,分,時,日,月,周,每個都是uint64
  // Dom:Day of Month,Dow:Day of week
  Second, Minute, Hour, Dom, Month, Dow uint64
}
 
// bounds provides a range of acceptable values (plus a map of name to value).
// 定義了表達式的結構體
type bounds struct {
  min, max uint
  names  map[string]uint
}
 
 
// The bounds for each field.
// 這樣就能看出各個表達式的范圍
var (
    seconds = bounds{0, 59, nil}
    minutes = bounds{0, 59, nil}
    hours  = bounds{0, 23, nil}
    dom   = bounds{1, 31, nil}
    months = bounds{1, 12, map[string]uint{
       "jan": 1,
       "feb": 2,
       "mar": 3,
       "apr": 4,
       "may": 5,
       "jun": 6,
       "jul": 7,
       "aug": 8,
       "sep": 9,
       "oct": 10,
       "nov": 11,
       "dec": 12,
    }}
    dow = bounds{0, 6, map[string]uint{
       "sun": 0,
       "mon": 1,
       "tue": 2,
       "wed": 3,
       "thu": 4,
       "fri": 5,
       "sat": 6,
    }}
)
 
const (
    // Set the top bit if a star was included in the expression.
    starBit = 1 << 63
)

看了上面的東西肯定有人疑惑為什么秒分時這些都是定義了unit64,以及定義了一個常量starBit = 1 << 63這種寫法,這是邏輯運算符。表示二進制1向左移動63位。原因如下:

cron表達式是用來表示一系列時間的,而時間是無法逃脫自己的區間的 , 分,秒 0 - 59 , 時 0 - 23 , 天/月 0 - 31 , 天/周 0 - 6 , 月0 - 11 。 這些本質上都是一個點集合,或者說是一個整數區間。 那么對于任意的整數區間 , 可以描述cron的如下部分規則。

  1. * | ? 任意 , 對應區間上的所有點。 ( 額外注意 日/周 , 日 / 月 的相互干擾。)

  2. 純數字 , 對應一個具體的點。

  3. / 分割的兩個數字 a , b, 區間上符合 a + n * b 的所有點 ( n >= 0 )。

  4. - 分割的兩個數字, 對應這兩個數字決定的區間內的所有點。

  5. L | W 需要對于特定的時間特殊判斷, 無法通用的對應到區間上的點。

至此, robfig/cron為什么不支持 L | W的原因已經明了了。去除這兩條規則后, 其余的規則其實完全可以使用點的窮舉來通用表示。 考慮到最大的區間也不過是60個點,那么使用一個uint64的整數的每一位來表示一個點便很合適了。所以定義unit64不為過

下面是go中cron表達式的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
  ------------------------------------------------------------
  第64位標記任意 , 用于 日/周 , 日 / 月 的相互干擾。
  63 - 0 為 表示區間 [63 , 0] 的 每一個點。
  ------------------------------------------------------------
 
  假設區間是 0 - 63 , 則有如下的例子 :
 
  比如 0/3 的表示如下 : (表示每隔兩位為1)
  * / ?   
  +---+--------------------------------------------------------+
  | 0 | 1 0 0 1 0 0 1 ~~ ~~          1 0 0 1 0 0 1 |
  +---+--------------------------------------------------------+ 
    63 ~ ~                      ~~ 0
 
  比如 2-5 的表示如下 : (表示從右往左2-5位上都是1)
  * / ?   
  +---+--------------------------------------------------------+
  | 0 | 0 0 0 0 ~ ~   ~~      ~  0 0 0 1 1 1 1 0 0 |
  +---+--------------------------------------------------------+ 
    63 ~ ~                      ~~ 0
 
 比如 * 的表示如下 : (表示所有位置上都為1)
  * / ?   
  +---+--------------------------------------------------------+
  | 1 | 1 1 1 1 1 ~ ~         ~  1 1 1 1 1 1 1 1 1 |
  +---+--------------------------------------------------------+ 
    63 ~ ~                      ~~ 0
*/

parser.go

將字符串解析為SpecSchedule的類。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
package cron
 
import (
  "fmt"
  "math"
  "strconv"
  "strings"
  "time"
)
 
// Configuration options for creating a parser. Most options specify which
// fields should be included, while others enable features. If a field is not
// included the parser will assume a default value. These options do not change
// the order fields are parse in.
type ParseOption int
 
const (
  Second   ParseOption = 1 << iota // Seconds field, default 0
  Minute               // Minutes field, default 0
  Hour                // Hours field, default 0
  Dom                 // Day of month field, default *
  Month                // Month field, default *
  Dow                 // Day of week field, default *
  DowOptional             // Optional day of week field, default *
  Descriptor             // Allow descriptors such as @monthly, @weekly, etc.
)
 
var places = []ParseOption{
  Second,
  Minute,
  Hour,
  Dom,
  Month,
  Dow,
}
 
var defaults = []string{
  "0",
  "0",
  "0",
  "*",
  "*",
  "*",
}
 
// A custom Parser that can be configured.
type Parser struct {
  options  ParseOption
  optionals int
}
 
// Creates a custom Parser with custom options.
//
// // Standard parser without descriptors
// specParser := NewParser(Minute | Hour | Dom | Month | Dow)
// sched, err := specParser.Parse("0 0 15 */3 *")
//
// // Same as above, just excludes time fields
// subsParser := NewParser(Dom | Month | Dow)
// sched, err := specParser.Parse("15 */3 *")
//
// // Same as above, just makes Dow optional
// subsParser := NewParser(Dom | Month | DowOptional)
// sched, err := specParser.Parse("15 */3")
//
func NewParser(options ParseOption) Parser {
  optionals := 0
  if options&DowOptional > 0 {
    options |= Dow
    optionals++
  }
  return Parser{options, optionals}
}
 
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
// It accepts crontab specs and features configured by NewParser.
// 將字符串解析成為SpecSchedule 。 SpecSchedule符合Schedule接口
 
func (p Parser) Parse(spec string) (Schedule, error) {
  // 直接處理特殊的特殊的字符串
  if spec[0] == '@' && p.options&Descriptor > 0 {
    return parseDescriptor(spec)
  }
 
  // Figure out how many fields we need
  max := 0
  for _, place := range places {
    if p.options&place > 0 {
      max++
    }
  }
  min := max - p.optionals
 
  // cron利用空白拆解出獨立的items。
  fields := strings.Fields(spec)
 
  // 驗證表達式取值范圍
  if count := len(fields); count < min || count > max {
    if min == max {
      return nil, fmt.Errorf("Expected exactly %d fields, found %d: %s", min, count, spec)
    }
    return nil, fmt.Errorf("Expected %d to %d fields, found %d: %s", min, max, count, spec)
  }
 
  // Fill in missing fields
  fields = expandFields(fields, p.options)
 
  var err error
  field := func(field string, r bounds) uint64 {
    if err != nil {
      return 0
    }
    var bits uint64
    bits, err = getField(field, r)
    return bits
  }
 
  var (
    second   = field(fields[0], seconds)
    minute   = field(fields[1], minutes)
    hour    = field(fields[2], hours)
    dayofmonth = field(fields[3], dom)
    month   = field(fields[4], months)
    dayofweek = field(fields[5], dow)
  )
  if err != nil {
    return nil, err
  }
  // 返回所需要的SpecSchedule
  return &SpecSchedule{
    Second: second,
    Minute: minute,
    Hour:  hour,
    Dom:  dayofmonth,
    Month: month,
    Dow:  dayofweek,
  }, nil
}
 
func expandFields(fields []string, options ParseOption) []string {
  n := 0
  count := len(fields)
  expFields := make([]string, len(places))
  copy(expFields, defaults)
  for i, place := range places {
    if options&place > 0 {
      expFields[i] = fields[n]
      n++
    }
    if n == count {
      break
    }
  }
  return expFields
}
 
var standardParser = NewParser(
  Minute | Hour | Dom | Month | Dow | Descriptor,
)
 
// ParseStandard returns a new crontab schedule representing the given standardSpec
// (https://en.wikipedia.org/wiki/Cron). It differs from Parse requiring to always
// pass 5 entries representing: minute, hour, day of month, month and day of week,
// in that order. It returns a descriptive error if the spec is not valid.
//
// It accepts
//  - Standard crontab specs, e.g. "* * * * ?"
//  - Descriptors, e.g. "@midnight", "@every 1h30m"
// 這里表示不僅可以使用cron表達式,也可以使用@midnight @every等方法
 
func ParseStandard(standardSpec string) (Schedule, error) {
  return standardParser.Parse(standardSpec)
}
 
var defaultParser = NewParser(
  Second | Minute | Hour | Dom | Month | DowOptional | Descriptor,
)
 
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
//
// It accepts
//  - Full crontab specs, e.g. "* * * * * ?"
//  - Descriptors, e.g. "@midnight", "@every 1h30m"
func Parse(spec string) (Schedule, error) {
  return defaultParser.Parse(spec)
}
 
// getField returns an Int with the bits set representing all of the times that
// the field represents or error parsing field value. A "field" is a comma-separated
// list of "ranges".
func getField(field string, r bounds) (uint64, error) {
  var bits uint64
  ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
  for _, expr := range ranges {
    bit, err := getRange(expr, r)
    if err != nil {
      return bits, err
    }
    bits |= bit
  }
  return bits, nil
}
 
// getRange returns the bits indicated by the given expression:
//  number | number "-" number [ "/" number ]
// or error parsing range.
func getRange(expr string, r bounds) (uint64, error) {
  var (
    start, end, step uint
    rangeAndStep   = strings.Split(expr, "/")
    lowAndHigh    = strings.Split(rangeAndStep[0], "-")
    singleDigit   = len(lowAndHigh) == 1
    err       error
  )
 
  var extra uint64
  if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
    start = r.min
    end = r.max
    extra = starBit
  } else {
    start, err = parseIntOrName(lowAndHigh[0], r.names)
    if err != nil {
      return 0, err
    }
    switch len(lowAndHigh) {
    case 1:
      end = start
    case 2:
      end, err = parseIntOrName(lowAndHigh[1], r.names)
      if err != nil {
        return 0, err
      }
    default:
      return 0, fmt.Errorf("Too many hyphens: %s", expr)
    }
  }
 
  switch len(rangeAndStep) {
  case 1:
    step = 1
  case 2:
    step, err = mustParseInt(rangeAndStep[1])
    if err != nil {
      return 0, err
    }
 
    // Special handling: "N/step" means "N-max/step".
    if singleDigit {
      end = r.max
    }
  default:
    return 0, fmt.Errorf("Too many slashes: %s", expr)
  }
 
  if start < r.min {
    return 0, fmt.Errorf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
  }
  if end > r.max {
    return 0, fmt.Errorf("End of range (%d) above maximum (%d): %s", end, r.max, expr)
  }
  if start > end {
    return 0, fmt.Errorf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
  }
  if step == 0 {
    return 0, fmt.Errorf("Step of range should be a positive number: %s", expr)
  }
 
  return getBits(start, end, step) | extra, nil
}
 
// parseIntOrName returns the (possibly-named) integer contained in expr.
func parseIntOrName(expr string, names map[string]uint) (uint, error) {
  if names != nil {
    if namedInt, ok := names[strings.ToLower(expr)]; ok {
      return namedInt, nil
    }
  }
  return mustParseInt(expr)
}
 
// mustParseInt parses the given expression as an int or returns an error.
func mustParseInt(expr string) (uint, error) {
  num, err := strconv.Atoi(expr)
  if err != nil {
    return 0, fmt.Errorf("Failed to parse int from %s: %s", expr, err)
  }
  if num < 0 {
    return 0, fmt.Errorf("Negative number (%d) not allowed: %s", num, expr)
  }
 
  return uint(num), nil
}
 
// getBits sets all bits in the range [min, max], modulo the given step size.
func getBits(min, max, step uint) uint64 {
  var bits uint64
 
  // If step is 1, use shifts.
  if step == 1 {
    return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
  }
 
  // Else, use a simple loop.
  for i := min; i <= max; i += step {
    bits |= 1 << i
  }
  return bits
}
 
// all returns all bits within the given bounds. (plus the star bit)
func all(r bounds) uint64 {
  return getBits(r.min, r.max, 1) | starBit
}
 
// parseDescriptor returns a predefined schedule for the expression, or error if none matches.
func parseDescriptor(descriptor string) (Schedule, error) {
  switch descriptor {
  case "@yearly", "@annually":
    return &SpecSchedule{
      Second: 1 << seconds.min,
      Minute: 1 << minutes.min,
      Hour:  1 << hours.min,
      Dom:  1 << dom.min,
      Month: 1 << months.min,
      Dow:  all(dow),
    }, nil
 
  case "@monthly":
    return &SpecSchedule{
      Second: 1 << seconds.min,
      Minute: 1 << minutes.min,
      Hour:  1 << hours.min,
      Dom:  1 << dom.min,
      Month: all(months),
      Dow:  all(dow),
    }, nil
 
  case "@weekly":
    return &SpecSchedule{
      Second: 1 << seconds.min,
      Minute: 1 << minutes.min,
      Hour:  1 << hours.min,
      Dom:  all(dom),
      Month: all(months),
      Dow:  1 << dow.min,
    }, nil
 
  case "@daily", "@midnight":
    return &SpecSchedule{
      Second: 1 << seconds.min,
      Minute: 1 << minutes.min,
      Hour:  1 << hours.min,
      Dom:  all(dom),
      Month: all(months),
      Dow:  all(dow),
    }, nil
 
  case "@hourly":
    return &SpecSchedule{
      Second: 1 << seconds.min,
      Minute: 1 << minutes.min,
      Hour:  all(hours),
      Dom:  all(dom),
      Month: all(months),
      Dow:  all(dow),
    }, nil
  }
 
  const every = "@every "
  if strings.HasPrefix(descriptor, every) {
    duration, err := time.ParseDuration(descriptor[len(every):])
    if err != nil {
      return nil, fmt.Errorf("Failed to parse duration %s: %s", descriptor, err)
    }
    return Every(duration), nil
  }
 
  return nil, fmt.Errorf("Unrecognized descriptor: %s", descriptor)
}

項目中應用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
  "github.com/robfig/cron"
  "log"
)
 
func main() {
  i := 0
  c := cron.New()
  spec := "*/5 * * * * ?"
  c.AddFunc(spec, func() {
    i++
    log.Println("cron running:", i)
  })
  c.AddFunc("@every 1h1m", func() {
    i++
    log.Println("cron running:", i)
  })
  c.Start()
}

注: @every 用法比較特殊,這是Go里面比較特色的用法。同樣的還有 @yearly @annually @monthly @weekly @daily @midnight @hourly 這里面就不一一贅述了。希望大家能夠自己探索。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://www.cnblogs.com/zuxingyu/p/6023919.html

延伸 · 閱讀

精彩推薦
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
主站蜘蛛池模板: 免费看黄色大片 | 国产成人在线视频播放 | 激情婷婷综合久久久久 | 青青在线视频免费 | 国产自拍专区 | a黄毛片 | 国内精品视频九九九九 | 九九九九在线视频播放 | 男人把j放进女人的p里视频 | 日本三级做a全过程在线观看 | 秋霞理论在一l级毛片 | 国产欧美成人免费观看 | 久久99亚洲AV无码四区碰碰 | 午夜精品久久久久久久99蜜桃 | 好猛好紧好硬使劲好大刺激视频 | av在线亚洲男人的天堂 | 九九精品视频一区二区三区 | 水多多凹凸福利视频导航 | 包臀裙女教师波多野结衣 | 欧美人妖草草xxoo | 美女扒开胸罩露出胸大乳 | 欧美色fx性乌克兰 | 国产区成人精品视频 | 狠狠干在线观看 | 日本ww视频 | 美女视频久久 | 媳妇和公公小说 | www在线视频在线播放 | 国产一卡| 美女视频ww8888网网 | 8mav福利视频 | 高h全肉np触手 | 99热这里只有精品在线观看 | 色婷婷综合久久久中文字幕 | 亚洲国产精久久久久久久 | 日本护士xxxx视频免费 | 国产精品久久久久网站 | 女人国产香蕉久久精品 | 国产在线观看精品 | 岛国虐乳紧缚媚药调教 | 亚洲成av人影院 |