欢迎大家来到IT世界,在知识的湖畔探索吧!
以下文章来源于一艘慢船 ,作者一艘慢船
最近发现 job[1] 有很多 clone。这个写了快一年的项目当时的目的是为了做压力测试。
现在回看整个项目与代码都存在太多的冗余,所以做了一下重构。重构之后的项目,全部代码也就 80 行左右。
主要功能其实都已经在 routine[2] 中实现了, job[3] 其实只是做了一个简单的组装而已。
所以,也就借着这次重构,向大家介绍一下,这个压箱底的库 routine[4]. 这个库自从写出来以后,基本上我所有的 Go 项目都会使用。
routine 做了什么
为什么要写这么一个基础库呢?
每段程序都是从 main 函数开始的,但是,却不会将整个程序的功能实现都放在 main 函数里。而是,通过层层功能的抽象与封装,最终,在 main 函数仅提供功能函数的入口。所以,当我们看很多大型程序时,其实,main 函数是非常简单的。
但是,即使 main 函数越变越简单,有些必要的功能则是逃不掉的。例如程序启动参数、程序的信号处理,这些通常还是会放在 main 函数进行处理。
除了以上 main 函数本身的处理以外,我发现将程序中的执行绪,也就是固定的 Go 协程的入口放在 main 函数中进行定义,可以帮助维护者更加快速的理解应用的逻辑实现。
所以,我就写了这样一个基础库,我只需要将具体的功能实现作为 Executor 接口的实现即可。使用起来就是这样:
package main
import (
"log"
"context"
"github.com/x-mod/routine"
)
func main(){
//功能实现
YourExecutor := routine.Command("echo", routine.ARG("hello routine!"))
//Main封装
if err := routine.Main(
context.TODO(),
routine.Executor(YourExecutor),
routine.Signal(syscall.SIGINT, routine.SigHandler(func() {
os.Exit(1)
})),
); err != nil {
log.Fatal(err)
}
}
欢迎大家来到IT世界,在知识的湖畔探索吧!
routine 协程控制
很多刚刚初级 Go 语言开发人员,常常在控制协程犯如下错误:
欢迎大家来到IT世界,在知识的湖畔探索吧!wg sync.WaitGroup
wg.Add(1) //! 正确写法
go func(){
wg.Add(1) //! 错误写法
defer wg.Done()
...
}()
wg.Wait()
提供一个简单协程控制的基础库,可以规避类似错误。
整个routine的协程逻辑可以通过下图进行一个简单的展示。
routine 执行器
routine 库除了提供一个 main 函数的基础框架与协程控制以外,还提供了很多功能函数的执行器。包括:
- 重试执行 retry
- 重复执行 repeat
- 计划执行 crontab
- 并发执行 concurrent
等等,很多功能性封装。
import "github.com/x-mod/routine"
//timeout
timeout := routine.Timeout(time.Minute, exec)
//retry
retry := routine.Retry(3, exec)
//repeat
repeat := routine.Repeat(10, time.Second, exec)
//concurrent
concurrent := routine.Concurrent(4, exec)
//schedule executor
crontab := routine.Crontab("* * * * *", exec)
//command
command := routine.Command("echo", routine.ARG("hello routine!"))
//parallel
parallel := routine.Parallel(exec1, exec2, exec3, ...)
//sequence
sequece := routine.Append(exec1, exec2, exec3, ...)
routine 实现 job
所以,有了 routine 库以后,重构job的功能,就非常简单,真的仅仅只是简单的组装代码而已了。
不妨看一下, job的核心代码:
欢迎大家来到IT世界,在知识的湖畔探索吧!func Main(c *cmd.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("job command required")
}
cmdOpts := []routine.CommandOpt{}
for index, argument := range args {
if index >= 1 {
cmdOpts = append(cmdOpts, routine.ARG(argument))
}
}
command := routine.Executor(routine.Command(args[0], cmdOpts...))
if viper.GetDuration("cmd-timeout") > 0 {
command = routine.Timeout(viper.GetDuration("cmd-timeout"), command)
}
if viper.GetInt("retry") > 0 {
command = routine.Retry(viper.GetInt("retry"), command)
}
if viper.GetInt("repeat-times") > 0 {
command = routine.Repeat(
viper.GetInt("repeat-times"),
viper.GetDuration("repeat-interval"),
command)
}
if viper.GetInt("concurrent") > 0 {
command = routine.Concurrent(viper.GetInt("concurrent"), command)
}
if viper.GetDuration("job-timeout") > 0 {
command = routine.Timeout(viper.GetDuration("job-timeout"), command)
}
if len(viper.GetString("schedule")) > 0 {
command = routine.Crontab(viper.GetString("schedule"), command)
}
ctx, cancel := context.WithCancel(context.TODO())
return routine.Main(
ctx,
command,
routine.Signal(syscall.SIGINT, routine.SigHandler(func() {
cancel()
})),
)
}
当然,job 项目中还用到了,我封装的另外一个库 cmd[5],通过这个库可以快速的实现一个命令行程序。感兴趣的同学不妨参考一下。
参考资料
[1]
job: https://github.com/liujianping/job
[2]
routine: https://github.com/x-mod/routine
[3]
job: https://github.com/liujianping/job
[4]
routine: https://github.com/x-mod/routine
[5]
cmd: https://github.com/x-mod/cmd
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/40819.html