1 cron表达式

CRON表达式表达式在线工具:https://tool.lu/crontab/

里面涵盖了
Linux和Java(Quartz)两种cron表达式的规范。

crontab里面的是Linux规范,对于表达式支持的符号相对Quartz少。

1.1 Linux标准

crontab只支持*,-/四种字符,权威的crontab在线表达式:https://crontab.guru/

字符 含义
* 全部。意思是在该时间的任意点都应当执行
, 多个值的分隔符,例如1,5,10
- 代表连续值,例如1-20
/ 步长。例如 5/15,代表从5开始,以15为步长。因此,当5/15位于分钟的位置时,表示小时内的第5、20、35和50分钟。

所以如果使用crontab来表达:

  1. 每月最后一天

每个月底最后一分钟执行某个命令

每个月底执行任务,则需要使用使用组合方式,

59 23 28-31 [ ‘date +%d -d tomorrow’ = 01 ] && command


打印明天日期

date +%d -d tomorrow

打印昨天日期

date +%d -d yesterday

日与星期的关系误区

(1)设置任务一:每月的1-7每天零时执行某任务

0 0 1-7 date > /tmp/date.txt

执行正常

(2)设置任务二:每星期的星期一零时执行某任务

0 0 1 date > /tmp/date.txt

执行正常

(3)第三个任务,设置每个月的第一个星期一零时执行某任务。

0 0 1-7 * 1 date > /tmp/date.txt

但实际执行的情况是

then at 2020-01-01 00:00:00 then at 2020-01-02 00:00:00 then at 2020-01-03 00:00:00 then at 2020-01-04 00:00:00 then at 2020-01-05 00:00:00 then at 2020-01-06 00:00:00 then at 2020-01-07 00:00:00 then at 2020-01-13 00:00:00

也就是说不是周一也执行了。
也就是说当日期和周几不为*时,则出现了或的情况,即是在日期或者周几设置范围内都触发任务。

1.2 Quartz标准

参考https://tool.lu/crontab

星期天为1,周一周六为2-7 。
这部分的表达式在: https://tool.lu/crontab/连接使用Java(Quartz)格式进行测试。

  1. 0 0 12 * * ? 每天中午12点触发
  2. 0 15 10 ? * * 每天上午10:15
  3. 0 15 10 * * ? 每天上午10:15触发
  4. 0 15 10 * * ? * 每天上午10:15触发
  5. 0 15 10 * * ? 2005 2005年的每天上午10:15触发
  6. 0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
  7. 0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
  8. 0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
  9. 0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
  10. 0 10,44 14 ? 3 WED 每年三月的星期三的下午2:102:44触发
  11. 0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
  12. 0 15 10 15 * ? 每月15日上午10:15触发
  13. 0 15 10 L * ? 每月最后一日的上午10:15触发
  14. 0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
  15. 0 15 10 ? * 6L 2020-2022 2020年至2022年的每月的最后一个星期五上午10:15触发
  16. 0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发

2 gocron搭建

2.1 go环境搭建(ubuntu和centos都使用源码安装)

以下以ubuntu为例

创建go工作目录(注意自己的路径,不要照抄我的路径)

  1. #创建go工作目录,创建后在自己home目录下
  2. mkdir -p ~/goworkspace/src
  3. cd ~/goworkspace
  4. wget https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz
  5. sudo tar zxvf go1.12.5.linux-amd64.tar.gz -C /usr/local
  6. #配置环境变量:在/etc/profile文件最后增加一行:
  7. sudo vim /etc/profile

添加PATH和GOPATH,GOPATH不要照抄路径

  1. export PATH=$PATH:/usr/local/go/bin
  2. export GOPATH=/home/lqf/goworkspace
  3. export GOROOT=/usr/local/go
  4. export GO111MODULE=auto
  5. export GOPROXY=http://mirrors.aliyun.com/goproxy/

然后执行

  1. source /etc/profile

查看go版本

  1. go version

显示版本号:go version go1.12.5 linux/amd64


2.2 goron安装

对于云服务器注意端口的开放。
gocron地址:https://github.com/ouqiang/gocron/

在go环境安装好后,进入到src目录

  1. # 进到gopath目录
  2. cd $GOPATH
  3. # 下载源码
  4. go get -d github.com/ouqiang/gocron
  5. # 下载完后进到
  6. cd $GOPATH/src/github.com/ouqiang/gocron
  7. # 开启mod
  8. export GO111MODULE=on
  9. # 删除原来的go.mod和go.sum
  10. rm -f go.mod go.sum
  11. # 重新生成go mod
  12. go mod init

用下面的内容替换go.mod里面的内容

  1. module github.com/ouqiang/gocron
  2. go 1.12
  3. require (
  4. github.com/Tang-RoseChild/mahonia v0.0.0-20131226213531-0eef680515cc
  5. github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 // indirect
  6. github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575
  7. github.com/dgrijalva/jwt-go v3.2.0+incompatible
  8. github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
  9. github.com/go-macaron/binding v0.0.0-20170611065819-ac54ee249c27
  10. github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
  11. github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 // indirect
  12. github.com/go-macaron/toolbox v0.0.0-20180818072302-a77f45a7ce90
  13. github.com/go-sql-driver/mysql v1.4.1
  14. github.com/go-xorm/builder v0.3.4 // indirect
  15. github.com/go-xorm/core v0.6.2
  16. github.com/go-xorm/xorm v0.7.1
  17. github.com/golang/protobuf v1.3.2
  18. github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5
  19. github.com/klauspost/compress v0.0.0-00010101000000-000000000000 // indirect
  20. github.com/klauspost/cpuid v1.2.1 // indirect
  21. github.com/klauspost/crc32 v1.2.0 // indirect
  22. github.com/lib/pq v1.1.1
  23. github.com/ouqiang/goutil v1.1.1
  24. github.com/rakyll/statik v0.1.6
  25. github.com/sirupsen/logrus v1.4.2
  26. github.com/urfave/cli v1.20.0
  27. golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
  28. google.golang.org/grpc v1.26.0
  29. gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
  30. gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
  31. gopkg.in/ini.v1 v1.42.0
  32. gopkg.in/macaron.v1 v1.3.2
  33. )
  34. replace (
  35. cloud.google.com/go => github.com/googleapis/google-cloud-go v0.50.0
  36. cloud.google.com/go/bigquery => github.com/googleapis/google-cloud-go/bigquery v1.3.0
  37. cloud.google.com/go/datastore => github.com/googleapis/google-cloud-go/datastore v1.0.0
  38. cloud.google.com/go/pubsub => github.com/googleapis/google-cloud-go/pubsub v1.1.0
  39. cloud.google.com/go/storage => github.com/googleapis/google-cloud-go/storage v1.4.0
  40. github.com/klauspost/compress => gitee.com/longjohnsilver/compress v1.2.1
  41. golang.org/x/crypto => github.com/golang/crypto v0.0.0-20191227163750-53104e6ec876
  42. golang.org/x/exp => github.com/golang/exp v0.0.0-20191227195350-da58074b4299
  43. golang.org/x/image => github.com/golang/image v0.0.0-20191214001246-9130b4cfad52
  44. golang.org/x/lint => github.com/golang/lint v0.0.0-20191125180803-fdd1cda4f05f
  45. golang.org/x/mobile => github.com/golang/mobile v0.0.0-20191210151939-1a1fef82734d
  46. golang.org/x/mod => github.com/golang/mod v0.1.0
  47. golang.org/x/net => github.com/golang/net v0.0.0-20191209160850-c0dbc17a3553
  48. golang.org/x/oauth2 => github.com/golang/oauth2 v0.0.0-20191202225959-858c2ad4c8b6
  49. golang.org/x/sync => github.com/golang/sync v0.0.0-20190911185100-cd5d95a43a6e
  50. golang.org/x/sys => github.com/golang/sys v0.0.0-20200102141924-c96a22e43c9c
  51. golang.org/x/text => github.com/golang/text v0.3.2
  52. golang.org/x/time => github.com/golang/time v0.0.0-20191024005414-555d28b269f0
  53. golang.org/x/tools => github.com/golang/tools v0.0.0-20200102200121-6de373a2766c
  54. golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20191204190536-9bdfabe68543
  55. google.golang.org/api => github.com/googleapis/google-api-go-client v0.15.0
  56. google.golang.org/appengine => github.com/golang/appengine v1.6.5
  57. google.golang.org/genproto => github.com/google/go-genproto v0.0.0-20191230161307-f3c370f40bfb
  58. google.golang.org/grpc => github.com/grpc/grpc-go v1.26.0
  59. )

然后进行编译

make

正常编译在最终打印

go build -o bin/gocron ./cmd/gocron go build -o bin/gocron-node ./cmd/node

启动应用
启动前需要安装mysql,请知悉**

  • web控制中心 gocron ./bin/gocron web
  • 分布式任务节点 gocron-node ./bin/gocron-node

gocron 默认使用5920端口
gocron-node默认使用5921端口

具体的参数可以使用gocron -h和gocron-node -h查看相应的说明。

3 python进程监控

发送邮件间隔问题
见 server_monitor.py的monitorProcess 进程拉起失败后降低监控频率

def monitorProcess(process, cfg, state):
    # 进程拉起失败后降低监控频率
    if not state['isAlive'] and state['skipTimes'] < math.pow(2, state['startTimes']) and state['skipTimes'] < 10:
        state['skipTimes'] += 1
        return
    if isProcessAlive(cfg.get(process, 'aliveCmd')):
        state['isAlive'] = True
        state['startTimes'] = 0
        state['skipTimes'] = 0                                      
        return 
    # 2.2 进程挂了 重启进程 
    logger.info(process + ' is not alive')      
    print(process + ' is not alive')                  
    # 已打开的日志文件描述符fd将传递给nginx进程!                  
    startProcess(cfg.get(process, 'startCmd'))                 
    time.sleep(2) # 过了一段时间再去检测进程是否真正的启动了
    if isProcessAlive(cfg.get(process, 'aliveCmd')):
        logger.info('start ' + process + ' succeed')
        print('start ' + process + ' succeed')
        # 3.1 进程重启成功
        sendMail(cfg, process, process + ' sendMailed, restart ok') # 重启成功
        state['isAlive'] = True
        state['startTimes'] = 0
        state['skipTimes'] = 0
    else:
        # 3.2 进程重启失败
        logger.info('start ' + process + ' failed')
        print('start ' + process + ' failed')
        now = int(time.time())
        #print now , state['lastReportTime'] + SECONDS_OF_MIN * cfg.getint(process, 'reportInterval')
        if state['isAlive'] or now > state['lastReportTime'] + SECONDS_OF_MIN * cfg.getint(process, 'reportInterval'):
            sendMail(cfg, process, process + ' died, restart fail')
            state['lastReportTime'] = now

        state['isAlive'] = False
        state['startTimes'] += 1
        state['skipTimes'] = 1

go参考

go mod常用命令 以及 常见问题 https://blog.csdn.net/zzhongcy/article/details/97243826