广告位联系
返回顶部
分享到

利用Go语言快速实现一个极简任务调度系统

Golang 来源:互联网 作者:佚名 发布时间:2022-10-06 10:48:17 人浏览
摘要

任务调度(Task Scheduling)是很多软件系统中的重要组成部分,字面上的意思是按照一定要求分配运行一些通常时间较长的脚本或程序。在爬虫管理平台Crawlab中,任务调度是其中的核心模

任务调度(Task Scheduling)是很多软件系统中的重要组成部分,字面上的意思是按照一定要求分配运行一些通常时间较长的脚本或程序。在爬虫管理平台 Crawlab 中,任务调度是其中的核心模块,相信不少朋友会好奇如何编写一个任务调度系统。本篇文章会教读者用 Go 语言编写一个非常简单的任务调度系统。

思路

我们首先理清一下思路,开发最小化任务调度器需要什么。

  • 交互界面(API)
  • 定时任务(Cron)
  • 任务执行(Execute Tasks)

整个流程如下:

我们通过 API 创建定时任务,执行器根据定时任务标准定期执行脚本。

实战

交互界面

首先我们来搭个架子。在项目目录下创建一个 main.go 文件,并输入以下内容。其中 gin 是非常流行的 Go 语言 API 引擎。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package main

?

import (

  "fmt"

  "github.com/gin-gonic/gin"

  "os"

)

?

func main() {

  // api engine

  app := gin.New()

?

  // api routes

  app.GET("/jobs", GetJobs)

  app.POST("/jobs", AddJob)

  app.DELETE("/jobs", DeleteJob)

?

  // run api on port 9092

  if err := app.Run(":9092"); err != nil {

    _, err = fmt.Fprintln(os.Stderr, err)

    os.Exit(1)

  }

}

然后添加 api.go 文件,输入以下内容,注意,这里没有任何代码实现,只是加入了占位区域。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package main

?

import "github.com/gin-gonic/gin"

?

func GetJobs(c *gin.Context) {

  // TODO: implementation here

}

?

func AddJob(c *gin.Context) {

  // TODO: implementation here

}

?

func DeleteJob(c *gin.Context) {

  // TODO: implementation here

}

定时任务

然后是任务调度的核心,定时任务。这里我们使用 robfig/cron,Go 语言比较流行的定时任务库。

现在创建 cron.go 文件,输入以下内容。其中 Cron 就是 robfig/cron 库中的 Cron 类生成的实例。

1

2

3

4

5

6

7

8

9

10

11

package main

?

import "github.com/robfig/cron"

?

func init() {

  // start to run

  Cron.Run()

}

?

// Cron create a new cron.Cron instance

var Cron = cron.New()

现在创建好了主要定时任务实例,就可以将核心逻辑添加在刚才的 API 占位区域了。

同样是 api.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

53

54

55

56

57

58

59

60

61

62

package main

?

import (

  "github.com/gin-gonic/gin"

  "github.com/robfig/cron/v3"

  "net/http"

  "strconv"

)

?

func GetJobs(c *gin.Context) {

  // return a list of cron job entries

  var results []map[string]interface{}

  for _, e := range Cron.Entries() {

    results = append(results, map[string]interface{}{

      "id":   e.ID,

      "next": e.Next,

    })

  }

  c.JSON(http.StatusOK, Cron.Entries())

}

?

func AddJob(c *gin.Context) {

  // post JSON payload

  var payload struct {

    Cron string `json:"cron"`

    Exec string `json:"exec"`

  }

  if err := c.ShouldBindJSON(&payload); err != nil {

    c.AbortWithStatus(http.StatusBadRequest)

    return

  }

?

  // add cron job

  eid, err := Cron.AddFunc(payload.Cron, func() {

    // TODO: implementation here

  })

  if err != nil {

    c.AbortWithStatusJSON(http.StatusInternalServerError, map[string]interface{}{

      "error": err.Error(),

    })

    return

  }

?

  c.AbortWithStatusJSON(http.StatusOK, map[string]interface{}{

    "id": eid,

  })

}

?

func DeleteJob(c *gin.Context) {

  // cron job entry id

  id := c.Param("id")

  eid, err := strconv.Atoi(id)

  if err != nil {

    c.AbortWithStatus(http.StatusBadRequest)

    return

  }

?

  // remove cron job

  Cron.Remove(cron.EntryID(eid))

?

  c.AbortWithStatus(http.StatusOK)

}

在这段代码中,我们实现了大部分逻辑,只在 AddJob 的 Cron.AddFunc 中第二个参数里,剩下最后一部分执行任务的代码。下面将来实现一下。

任务执行

现在需要添加任务执行的代码逻辑,咱们创建 exec.go 文件,输入以下内容。这里我们用到了 Go 语言内置的 shell 运行管理库 os/exec,可以执行任何 shell 命令。

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

package main

?

import (

  "fmt"

  "os"

  "os/exec"

  "strings"

)

?

func ExecuteTask(execCmd string) {

  // execute command string parts, delimited by space

  execParts := strings.Split(execCmd, " ")

?

  // executable name

  execName := execParts[0]

?

  // execute command parameters

  execParams := strings.Join(execParts[1:], " ")

?

  // execute command instance

  cmd := exec.Command(execName, execParams)

?

  // run execute command instance

  if err := cmd.Run(); err != nil {

    _, err = fmt.Fprintln(os.Stderr, err)

    fmt.Println(err.Error())

  }

}

好了,现在我们将这部分执行代码逻辑放到之前的占位区域中。

1

2

3

4

5

6

...

  // add cron job

  eid, _ := Cron.AddFunc(payload.Cron, func() {

    ExecuteTask(payload.Exec)

  })

...

代码效果

OK,大功告成!现在我们可以试试运行这个极简的任务调度器了。

在命令行中敲入 go run .,API 引擎就启动起来了。

1

2

3

4

5

6

7

8

9

10

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.

 - using env:   export GIN_MODE=release

 - using code:  gin.SetMode(gin.ReleaseMode)

?

[GIN-debug] GET    /jobs                     --> main.GetJobs (1 handlers)

[GIN-debug] POST   /jobs                     --> main.AddJob (1 handlers)

[GIN-debug] DELETE /jobs/:id                 --> main.DeleteJob (1 handlers)

[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.

Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.

[GIN-debug] Listening and serving HTTP on :9092

现在打开另一个命令行窗口,输入 curl -X POST -d '{"cron":"* * * * *","exec":"touch /tmp/hello.txt"}' http://localhost:9092/jobs,会得到如下返回结果。表示已经生成了相应的定时任务,任务 ID 为 1,每分钟跑一次,会更新一次 /tmp/hello.txt。

{"id":1}

在这个命令行窗口中输入 curl http://localhost:9092/jobs。

[{"id":1,"next":"2022-10-03T12:46:00+08:00"}]

这表示下一次执行是 1 分钟之后。

等待一分钟,执行 ls -l /tmp/hello.txt,得到如下结果。

-rw-r--r--  1 marvzhang  wheel     0B Oct  3 12:46 /tmp/hello.txt

也就是说,执行成功了,大功告成!

总结

本篇文章通过将 Go 语言几个库简单组合,就开发出了一个极简的任务调度系统。所用到的核心库:

  • gin
  • robfig/cron
  • os/exec

整个代码示例仓库在 GitHub 上: https://github.com/tikazyq/codao-code/tree/main/2022-10/go-task-scheduler


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7150156954394951694
相关文章
  • 基于GORM实现CreateOrUpdate的方法
    CreateOrUpdate 是业务开发中很常见的场景,我们支持用户对某个业务实体进行创建/配置。希望实现的 repository 接口要达到以下两个要求: 如果
  • Golang中的内存逃逸的介绍
    什么是内存逃逸分析 内存逃逸分析是go的编译器在编译期间,根据变量的类型和作用域,确定变量是堆上还是栈上 简单说就是编译器在编译
  • Golang自旋锁的介绍
    自旋锁 获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。 它是为实现保护共享资源而提出的
  • Go语言读写锁RWMutex的源码

    Go语言读写锁RWMutex的源码
    在前面两篇文章中初见 Go Mutex、Go Mutex 源码详解,我们学习了Go语言中的Mutex,它是一把互斥锁,每次只允许一个goroutine进入临界区,可以保
  • Go项目实现优雅关机与平滑重启功能
    什么是优雅关机? 优雅关机就是服务端关机命令发出后不是立即关机,而是等待当前还在处理的请求全部处理完毕后再退出程序,是一种对
  • Go语言操作Excel利器之excelize类库的介绍
    在开发中一些需求需要通过程序操作excel文档,例如导出excel、导入excel、向excel文档中插入图片、表格和图表等信息,使用Excelize就可以方便
  • 利用Go语言快速实现一个极简任务调度系统

    利用Go语言快速实现一个极简任务调度系统
    任务调度(Task Scheduling)是很多软件系统中的重要组成部分,字面上的意思是按照一定要求分配运行一些通常时间较长的脚本或程序。在爬
  • GoLang中的iface 和 eface 的区别介绍

    GoLang中的iface 和 eface 的区别介绍
    GoLang之iface 和 eface 的区别是什么? iface和eface都是 Go 中描述接口的底层结构体,区别在于iface描述的接口包含方法,而eface则是不包含任何方
  • Golang接口使用的教程
    go语言并没有面向对象的相关概念,go语言提到的接口和java、c++等语言提到的接口不同,它不会显示的说明实现了接口,没有继承、子类、
  • go colly 爬虫实现示例介绍
    贡献某CC,go源码爬虫一个,基于colly,效果是根据输入的浏览器cookie及excel必要行列号,从excel中读取公司名称,查询公司法人及电话号码。
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计