Month: 九月 2020

Web Cron 市场调研

因为实现了 sche 的语法,感觉完爆 cron,所以调研了一下 web cron,不过似乎市场不大。这个算是 unboundlling AWS 的一种,但是功能太简单了,直接用 AWS 也不复杂。而且竞争对手太多了。。

Heroku 真的是个很有意思的服务,解决了很多痛点,但是项目还是挺大的。不过值得思考的是,为什么 Heroku 做起来了,而 Google App Engine 凉了?是因为 GAE 超前时代太多了吗?

Sche.io 还没有被注册,可惜 99 刀太贵了,不然先注册一把。

  1. Indie Hacker 上有人有类似的想法。https://www.indiehackers.com/forum/cloud-based-cron-job-should-i-built-it-c46f58e66f
  2. Hacker News 上的评论大多是正面的,但是也不知道会不会付费。https://news.ycombinator.com/item?id=17346616
  3. 直接用 aws lambda 就行了。。https://gist.github.com/milesw/83332215df29fa25239712cd1ba273d9
  4. 这个 Cronhub 号称挣钱了。但是没看出哪里有意思或者可复制来。https://www.indiehackers.com/@tigran/cronhub-2nd-month-report-9e474add23

提到的一些痛点:

  1. 如何做好授权?
  2. 不一定所有服务都暴露了外部 http 接口
  3. 对于长时间执行的任务怎么办
  4. 能否直接 ssh 上去执行任务呢?
  5. 能否提供 API 让用户通过代码上传任务

如果真要做的话,应该把监控、日志统计等都做了。做到比 AWS、Azure、Heroku 的调度功能更加方便。

比较有意思的思考

网站的监控是一个挺大的领域

  1. 包括网站的功能监控,比如 Pingdom,HyperPing,PagerDuty,DataDog 等等
  2. 网站的用户监控,Google Analytics 和一系列的工具

关于 Heroku vs App Engine

这篇文章说的不错。Google 的第一个问题在于没有把用户放在心里,而是在 Demo 甚至 Show off 自己的 Infra。比如说:

  • 在 GAE 上用户需要大幅度修改自己的代码才能运行
  • 没有 SQL,只能用 Google 自己的 BigTable
  • 不支持最最最流行的 PHP

等等。。

而在 Heroku 上,用户可以随意使用已经很熟悉的 Postgres/Mongo/Redis/MySQL 等等数据库,也就是说迁移到 Heroku 几乎不费任何代价,而且还不用关心负载平衡、监控等运维细节。

另一方面,Heroku 最开始支持的是 Ruby on Rails,而这个社区在当时是非常活跃,而且乐于传教的。

最后一点,Google App Engine 甚至比 Heroku 贵不少。。说好的大厂不差钱呢?

sche – 一种人类能够看懂的 cron 语法

在 Linux 系统上,我们一般使用 cron 来设置定时任务,然而 cron 的语法还是有些佶屈聱牙的,几乎每次要修改的时候都需要查一下文档才知道什么意思,以至于有 crontab.guru 这种网站专门来解释 cron 的语法。

想象一下,能不能有一种让人一眼就能看懂的语法来表达周期性的调度操作呢?比如说这样:

every 10 minutes         , curl apple.com
every hour               , echo 'time to take some coffee'
every day at 10:30       , eat
every 5 to 10 minutes    , firefox http://news.ycombinator.com
every monday             , say 'Good week'
every wednesday at 13:15 , rm -rf /
every minute at :17      , ping apple.com
every 90 minutes         , echo 'time to stand up'

这样的配置文件是不是很容易懂呢?如果要写成 crontab 的格式大概是这样的:

*/10 * * * *    curl apple.com
0 * * * *       echo 'time to take some coffee'
30 10 * * *     eat
*/7 * * * *     firefox http://news.ycombinator.com  # 实际上是不对的,因为 cron 没法随机
0 0 * * MON     say 'Good week'
15 13 * * WED   rm -rf /
# every minute at :17  无法实现,因为 cron 中没有秒
0 0-21/3 * * *  echo 'time to stand up'  # 需要两条命令来完成每隔 90 分钟的操作
30 1-22/3 * * * echo 'time to stand up'

可以很明显看出,cron 的语法可读性还是差一些的,关键是维护起来更是像读天书一样。幸运的是,我在周末刚刚做了一个小工具,虽然还比较粗糙,但是也已经可以解析上面这种可读性比较好的语法。下面简单介绍一下如何使用:

介绍 sche

sche 是一个 Python 程序,所以可以使用 pip 直接安装:

pip install sche

安装之后,就会得到一个 sche 命令,帮助文件如下:

-> % sche -h
usage: sche [-h] [-f FILE] [-t]

A simple command like `cron`, but more human friendly.

The default configuration file is /etc/schetab, syntax goes like:

    # (optional) set time zone first
    timezone = +0800

    # line starts with # is a comment
    every 60 minutes, echo "wubba lubba dub dub"

    # backup database every day at midnight
    every day at 00:00, mysqldump -u backup

    # redirect logs so you can see them
    every minute, do_some_magic >> /some/output/file 2>&1

optional arguments:
  -h, --help            show this help message and exit
  -f FILE, --file FILE  configuration file to use
  -t, --test            test configuration and exit

我们只需要把需要执行的命令放到 /etc/schetab 文件下就好了,这里显然是在致敬 /etc/crontab。比如说:

-> % cat /etc/schetab
timzone = +0800
every 5 seconds, echo "wubba lubba dub dub"
every 10 seconds, date

-> % sche
wubba lubba dub dub
Tue Sep  1 22:15:01 CST 2020
wubba lubba dub dub
wubba lubba dub dub
Tue Sep  1 22:15:11 CST 2020
wubba lubba dub dub
wubba lubba dub dub
Tue Sep  1 22:15:21 CST 2020
wubba lubba dub dub

如何让 sche 像 cron 一样作为一个守护进程呢?秉承 Unix 一个命令只做一件事的哲学,sche 本身显然是不提供这个功能的,可以使用 systemd 实现,几行配置写个 unit 文件就搞定了。

sche 的来源

sche 是 schedule — 另一个 Python 库的一个 fork, schedule 支持这样的 Python 语句:

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)

然而我的需求是把时间配置独立出来,能够像 cron 一样存到一个文本文件里,而不是写成 Python 代码,于是提了一个 PR,增加了 when 这个方法来解析表达式。同时我还强烈需求时区支持,然而原版的 schedule 也不支持。所以就创建了一个 fork.

sche.when("every wednesday at 13:15").do(job)
sche.timezone("+0800").every().day.at("00:00").do(job)

最后,原生的 cron 命令实际上(至少我)已经极少用了,然而 crontab 的语法流传还是非常广的,在所有需要定时任务的地方,几乎都能看到 cron 的身影,比如说 Kubernetes job 等等,如果能够使用一种让正常人能随时看懂的语法,感觉还是很有意义的。

参考

  1. https://schedule.readthedocs.io/en/stable/
  2. https://crontab.guru/
  3. https://stackoverflow.com/q/247626/1061155