- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详解Gotorch多机定时任务管理系统由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
。
先介绍下问题:
组内有十来台机器,上面用 cron 分别定时执行着一些脚本和 shell 命令,一开始任务少的时候,大家都记得哪台机器执行着什么,随着时间推移,人员几经变动,任务也越来越多,再也没人能记得清哪些任务在哪些机器上执行了,排查和解决后台脚本的问题也越来越麻烦.
解决这个问题也不是没有办法:
除此之外,任务的修改也非常不方便,如果想给在 crontab 里修改某一项任务,还需要找运维操作。虽然解决这个问题也有办法,使用 crontab cronfile.txt 直接让 crontab 加载文件,但引入新的问题:任务文件加载的实时性不好控制.
为了解决以上问题,我结合 cron 和任务管理,每天下班后花一点时间,实现一个小功能,最后完成了 gotorch 的可用版。看着 GitHub 的 commit 统计,还挺有成就感的~ 。
这里放上 GitHub 链接地址: GitHub-zhenbianshu-gotorch ,欢迎 star/fork/issue.
介绍一下特色功能:
下面说一下功能实现的技术要点:
文章欢迎转载,但请带上本文源地址:http://www.cnblogs.com/zhenbianshu/p/7905678.html,谢谢.
。
在实现类似 cron 的功能之前,我简单地看了一下 cron 的源码,源码在 https://busybox.net/downloads/ 可以下载,解压后文件在miscutils > crond.c.
cron 的实现设计得很巧妙的,大概如下:
数据结构:
1.cron 拥有一个全局结构体 global ,保存着各个用户的任务列表; 。
2.每一个任务列表是一个结构体 CronFile, 保存着用户名和任务链表等; 。
3.每一个任务 CronLine 有 shell 命令、执行 pid、执行时间数组 cl_Time 等属性; 。
4.执行时间数组的最大长度根据 “分时日月周” 的最大值确定,将可执行时间点的值置为 true,例如 在每天的 3 点执行则 cl_Hrs[3]=true; 。
执行方式:
1.cron是一个 while(true) 式的长循环,每次 sleep 到下一分钟的开始.
2.cron 在每分钟的开始会依次遍历检查用户 cron 配置文件,将更新后的配置文件解析成任务存入全局结构体,同时它也定期检查配置文件是否被修改.
3.然后 cron 会将当前时间解析为 第 n 分/时/日/月/周,并判断 cal_Time[n] 全为 true 则执行任务.
4.执行任务时将 pid 写入防止重复执行; 。
5.后续 cron 还会进行一些异常检测和错误处理操作.
明白了 cron 的执行方式后,感觉每个时间单位都遍历任务进行判断于性能有损耗,而且我实现的是秒级执行,遍历判断的性能损耗更大,于是考虑优化成:
给每个任务设置一个 next_time 的时间戳,在一次执行后更新此时间戳,每个时间单位只需要判断 task.next_time == current_time.
后来由于 “秒分时日月周” 的日期格式进位不规则,代码太复杂,实现出来效率也不比原来好,终于放弃了这种想法。。采用了跟 cron 一样的执行思路.
此外,我添加了三种限制任务执行的方式:
而任务启动方式,则直接使用 goroutine 配合 exec 包,每次执行任务都启动一个新的 goroutine,保存 pid,同时进行错误处理。由于服务可能会在一秒内多次扫描任务,我给每个任务添加了一个进程上次执行时间戳的属性,待下次执行时对比,防止任务在一秒内多次扫描执行了多次.
。
本服务是做成了一个类似 nginx 的服务,我将进程的 pid 保存在一个临时文件中,对进程操作时通过命令行给进程发送信号,只需要注意下异常情况下及时清理 pid 文件就好了.
这里说一下 Go 守护进程的创建方式:
由于 Go 程序在启动时 runtime 可能会创建多个线程(用于内存管理,垃圾回收,goroutine管理等),而 fork 与多线程环境并不能和谐共存,所以 Go 中没有 Unix 系统中的 fork 方法;于是启动守护进程我采用 exec 之后立即执行,即 fork and exec 的方式,而 Go 的 exec 包则支持这种方式.
在进程最开始时获取并判断进程 ppid 是否为1 (守护进程的父进程退出,进程会被“过继”给 init 进程,其进程号为1),在父进程的进程号不为1时,使用原进程的所有参数 fork and exec 一个跟自己相同的进程,关闭新进程与终端的联系,并退出原进程.
filePath, _ := filepath.Abs(os.Args[0]) // 获取服务的命令路径cmd := exec.Command(filePath, os.Args[1:]...) // 使用自身的命令路径、参数创建一个新的命令cmd.Stdin = nilcmd.Stdout = nil cmd.Stderr = nil // 关闭进程标准输入、标准输出、错误输出cmd.Start() // 新进程执行return // 父进程退出
。
将进程制作为守护进程之后,进程与外界的通信就只好依靠信号了,Go 的 signal 包搭配 goroutine 可以方便地监听、处理信号。同时我们使用 syscall 包内的 Kill 方法来向进程发送信号.
我们监听 Kill 默认发送的信号SIGTERM,用来处理服务退出前的清理工作,另外我还使用了用户自定义信号SIGUSR2 用来作为终端通知服务重启的消息.
一个信号从监听到捕捉再到处理的完整流程如下:
1.首先我们使用创建一个类型为 os.Sygnal 的无缓冲channel,来存放信号.
2.使用 signal.Notify() 函数注册要监听的信号,传入刚创建的 channel,在捕捉到信号时接收信号.
3.创建一个 goroutine,在 channel 中没有信号时 signal := <-channel 会阻塞.
4.Go 程序一旦捕捉到正在监听的信号,就会把信号通过 channel 传递过来,此时 goroutine 便不会继续阻塞.
5.通过后面的代码处理对应的信号.
对应的代码如下:
c := make(chan os.Signal)signal.Notify(c, syscall.SIGTERM, syscall.SIGUSR2) // 开启一个goroutine异步处理信号go func() { s := <-c if s == syscall.SIGTERM { task.End() logger.Debug("bootstrap", "action: end", "pid "+strconv.Itoa(os.Getpid()), "signal "+fmt.Sprintf("%d", s)) os.Exit(0) } else if s == syscall.SIGUSR2 { task.End() bootStrap(true) }}()
。
gotorch 的开发共花了三个月,每天半小时左右,1~3 个 commits,经历了三次大的重构,特别是在代码格式上改得比较频繁。 不过使用 Go 开发确实是挺舒心的,Go 的代码很简洁, gofmt 用着非常方便。另外 Go 的学习曲线也挺平滑,熟悉各个常用标准包后就能进行简单的开发了。 简单易学、高效快捷,难怪 Go 火热得这么快了.
以上就是详解Gotorch多机定时任务管理系统的详细内容,更多关于Gotorch多机定时任务管理系统的资料请关注我其它相关文章! 。
最后此篇关于详解Gotorch多机定时任务管理系统的文章就讲到这里了,如果你想了解更多关于详解Gotorch多机定时任务管理系统的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Task.WaitAll 方法等待所有任务,Task.WaitAny 方法等待一个任务。如何等待任意N个任务? 用例:下载搜索结果页面,每个结果都需要一个单独的任务来下载和处理。如果我使用 WaitA
我正在查看一些像这样的遗留 C# 代码: await Task.Run(() => { _logger.LogException(LogLevel.Error, mes
如何在 Linux 中运行 cron 任务? 关注此Q&A ,我有这个 cron 任务要运行 - 只是将一些信息写入 txt 文件, // /var/www/cron.php $myfile = fo
原谅我的新手问题,但我想按顺序执行三个任务并在剧本中使用两个角色: 任务 角色 任务 角色 任务 这是我到目前为止(任务,角色,任务): --- - name: Task Role Task ho
我有一个依赖于 installDist 的自定义任务 - 不仅用于执行,还依赖于 installDist 输出: project.task('run', type: JavaExec, depends
从使用 Wix 创建的 MSI 运行卸载时,我需要在尝试删除任何文件之前强行终止在后台运行的进程。主要应用程序由一个托盘图标组成,它反射(reflect)了 bg 进程监控本地 Windows 服务的
我想编写 Ant 任务来自动执行启动服务器的任务,然后使用我的应用程序的 URL 打开 Internet Explorer。 显然我必须执行 startServer先任务,然后 startApplic
使用 ASP.NET 4.5,我正在尝试使用新的 async/await 玩具。我有一个 IDataReader 实现类,它包装了一个特定于供应商的阅读器(如 SqlDatareader)。我有一个简
使用命令 gradle tasks可以得到一份所有可用任务的报告。有什么方法可以向此命令添加参数并按任务组过滤任务。 我想发出类似 gradle tasks group:Demo 的命令筛选所有任务并
除了sshexec,还有什么办法吗?任务要做到这一点?我知道您可以使用 scp 复制文件任务。但是,我需要执行其他操作,例如检查是否存在某些文件夹,然后将其删除。我想使用类似 condition 的东
假设我有字符串 - "D:\ApEx_Schema\Functions\new.sql@@\main\ONEVIEW_Integration\3" 我需要将以下内容提取到 diff 变量中 - 文档名
我需要编写一个 ant 任务来确定某个文件是否是只读的,如果是,则失败。我想避免使用自定义选择器来为我们的构建系统的性质做这件事。任何人都有任何想法如何去做?我正在使用 ant 1.8 + ant-c
这是一个相当普遍的计算机科学问题,并不特定于任何操作系统或框架。 因此,我对与在线程池上切换任务相关的开销感到有些困惑。在许多情况下,给每个作业分配自己的特定线程是没有意义的(我们不想创建太多硬件线程
我正在使用以下 Ansible playbook 一次性关闭远程 Ubuntu 主机列表: - hosts: my_hosts become: yes remote_user: my_user
如何更改 Ant 中的当前工作目录? Ant documentation没有 任务,在我看来,最好的做法是不要更改当前工作目录。 但让我们假设我们仍然想这样做——你会如何做到这一点?谢谢! 最佳答案
是否可以运行 cronjob每三天一次?或者也许每月 10 次。 最佳答案 每三天运行一次 - 或更短时间在月底运行一次。 (如果上个月有 31 天,它将连续运行 2 天。) 0 0 */3 * *
如何在 Gradle 任务中执行托管在存储库中的工具? 在我的具体情况下,我正在使用 Gradle 构建一个 Android 应用程序。我添加了一项任务,将一些 protobuf 数据从文本编码为二进
我的项目有下一个结构: Root |- A |- C (depends on A) \- B (depends on A) 对于所有子项目,我们使用自己的插件生成资源:https://githu
我设置了一个具有4个节点的Hadoop群集,其中一个充当HDFS的NameNode以及Yarn主节点。该节点也是最强大的。 现在,我分发了2个文本文件,一个在node01(名称节点)上,一个在node
在 TFS 2010 中为多个用户存储任务的最佳方式是什么?我只能为一项任务分配一个。 (例如:当我计划向所有开发人员演示时) (这是一个 Scrum Msf 敏捷项目,其中任务是用户故事的一部分)
我是一名优秀的程序员,十分优秀!