Go 每日一库之 jobrunner
简介
我们在 Web 开发中时常会遇到这样的需求,执行一个操作之后,需要给用户一定形式的通知。例如,用户下单之后通过邮件发送电子发票,网上购票支付后通过短信发送车次信息。但是这类需求并不需要非常及时,如果放在请求流程中处理,会影响请求的响应时间。这类任务我们一般使用异步的方式来执行。jobrunner
就是其中一个用来执行异步任务的 Go 语言库。得益于强大的cron
库,再搭配jobrunner
的任务状态监控,jobrunner
非常易于使用。
快速使用
本文使用 Go Modules。
创建目录并初始化:
$ mkdir jobrunner && cd jobrunner
$ go mod init github.com/darjun/go-daily-lib/jobrunner
安装jobrunner
:
$ go get -u github.com/bamzi/jobrunner
使用:
package main
import (
"fmt"
"time"
"github.com/bamzi/jobrunner"
)
type GreetingJob struct {
Name string
}
func (g GreetingJob) Run() {
fmt.Println("Hello, ", g.Name)
}
func main() {
jobrunner.Start()
jobrunner.Schedule("@every 5s", GreetingJob{Name: "dj"})
time.Sleep(10 * time.Second)
}
我们创建一个任务,每隔 5s 打印一条欢迎信息。任务的创建和执行与cron
完全相同,详细使用见我前面的一篇博文。
注意,jobrunner
需要先Start()
,然后再添加任务。因为在Start()
中创建MainCron
对象,先添加任务会panic
!!!
注意main
函数尾的time.Sleep(10 * time.Second)
,因为主 goroutine 结束之后整个程序就退出了,jobrunner
中的任务就没有机会被执行了。加上time.Sleep
是为了让大家能看到输出,实际使用中不会这样做。
与 web 框架整合
jobrunner
能很方便地与当前常见的 Web 框架整合,如Gin/Echo/Martini/Beego/Revel
等。下面通过一个简单的例子演示如何在 Gin 中使用jobrunner
:用户登录时给他的邮箱发送一封邮件。
首先需要安装相应的库:
$ go get -u github.com/gin-gonic/gin
$ github.com/jordan-wright/email
编写代码:
package main
import (
"fmt"
"net/smtp"
"time"
"github.com/bamzi/jobrunner"
"github.com/gin-gonic/gin"
"github.com/jordan-wright/email"
)
type EmailJob struct {
Name string
Email string
}
type User struct {
Name string `form:"name"`
Email string `form:"email"`
}
func (j EmailJob) Run() {
e := email.NewEmail()
e.From = "leedarjun@126.com"
e.To = []string{j.Email}
e.Cc = []string{"leedarjun@126.com"}
e.Subject = "Welcome To Awesome-Web"
e.Text = []byte(fmt.Sprintf(`
Hello, %s
Welcome Back
`, j.Name))
err := e.Send("smtp.126.com:25", smtp.PlainAuth("", "leedarjun@126.com", "yyyyyy", "smtp.126.com"))
if err != nil {
fmt.Printf("failed to send email to %s, err:%v", j.Name, err)
}
}
func login(c *gin.Context) {
var u User
if c.ShouldBind(&u) == nil {
c.String(200, "login success")
jobrunner.In(5*time.Second, EmailJob{Name: u.Name, Email: u.Email})
} else {
c.String(404, "login failed")
}
}
func main() {
r := gin.Default()
r.GET("/login", login)
r.Run(":8888")
}
这里只是为了简单演示,我们编写了一个简陋的login
函数处理登录,传入name
和email
,然后给该email
发送邮件。email
库的详细使用可以查看我之前的博文了解。
只需要在浏览器中输入http://localhost:8888/login?name=dj&email=935653229@qq.com
,我的 QQ 邮箱就能收到邮件:
监控
jobrunner
内置了一个监控模块,可以很方便地通过网页或者 API 获取当前的任务状态数据:
package main
import (
"fmt"
"html/template"
"os"
"time"
"github.com/bamzi/jobrunner"
"github.com/gin-gonic/gin"
)
type GreetingJob struct {
Name string
}
func (g GreetingJob) Run() {
fmt.Println("Hello,", g.Name)
}
type EmailJob struct {
Email string
}
func (e EmailJob) Run() {
fmt.Println("Send,", e.Email)
}
func main() {
r := gin.Default()
jobrunner.Start()
jobrunner.Every(5*time.Second, GreetingJob{Name: "dj"})
jobrunner.Every(10*time.Second, EmailJob{Email: "935653229@qq.com"})
r.GET("/jobrunner/json", JobJson)
r.GET("/jobrunner/html", JobHtml)
r.Run(":8888")
}
func JobJson(c *gin.Context) {
c.JSON(200, jobrunner.StatusJson())
}
func JobHtml(c *gin.Context) {
t, err := template.ParseFiles(os.Getenv("GOPATH") + "/src/github.com/bamzi/jobrunner/views/Status.html")
if err != nil {
c.JSON(400, "error")
}
t.Execute(c.Writer, jobrunner.StatusPage())
}
运行之后,在浏览器中输入http://localhost:8888/jobrunner/html
查看任务状态:
这里显示任务名、任务 ID、状态、上次运行时间、下次运行时间以及处理延迟。
我们还可以通过http://localhost:8888/jobrunner/json
获取原始 JSON 格式的数据自己处理:
总结
大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue?
参考
- jobrunner GitHub:https://github.com/bamzi/jobrunner
- Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib
- 简单的说下,(function(){...})() 与 (function(){...}()) 有什么区别?
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇]
- Shell常用命令小结
- 插入法排序
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part II
- 震惊了!这样的js面试题让所有人-男默女泪
- 前端知识学了却不会用,都是没走心
- var a="xx";a=a+"ss";a的值变了,但"xx"字符串并没有变
- 先行者计划--1109微课总结 | 《通过二个demo初识webPack》
- 先行者计划--1107微课 《什么是Vuex?》| 文字简版
- 脱离前端菜鸟层次的二个关键点
- 【课堂笔记】先行者 3.0版本的vueJs课程的第三次课
- ASP.NET:创建Linked ValidationSummary, 深入理解ASP.NET的Validation
- 【课堂笔记】先行者 3.0版本的vueJs课程的第二次课
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Ubuntu 18.04 通过 Docker 快速部署 Smokeping 2.6.11 教程
- MySQL 8.0新特性 — 函数索引
- Docker快速上手指北(一)【技术创作101训练营】
- leetcode树之二叉搜索树的最近公共祖先
- 【技术创作101训练营】技术角 | 在CentOS 8上使用Nginx 1.18: 基本配置
- Java诊断应用之Arthas实战(技术创作101训练营)
- 突击并发编程JUC系列-数组类型AtomicLongArray
- 个人量化投资体系搭建(一)
- 服务端的 WebAssembly 与 Rust 入门篇
- pImpl
- Flask+requests发起页面请求示例
- 【技术创作101训练营】Git 如何成功配置SSH key连接多个代码平台?
- 深入浅出iOS内存管理-技术创作101训练营
- 聊聊原型 Prototype | 技术创作101训练营
- Excelize 2.3.1 发布,Go 语言 Excel 文档基础库,支持加密表格文档