libi / dcron

轻量分布式定时任务库 a lightweight distributed job scheduler library
MIT License
446 stars 76 forks source link

需求想法沟通 #25

Closed xiaofeige closed 1 year ago

xiaofeige commented 1 year ago

兄弟,既然是分布式定时器库,我觉得想法可以进一步展开。

  1. 新增个http这其他rpc接口,支持远程注册定时任务&参数,定时call相关接口
  2. 周期式执行/递推式执行周期任务是否可选
  3. 大量线上场景并非周期式定时任务,一段时间后触发的任务占绝大多数,能否支持
  4. 海量定时任务还可以继续优化,例如只缓存未来10min钟内要执行的定时任务,降低内存压力
libi commented 1 year ago

感谢建议!dcron开发最初的目标是在系统的单机 crontab 转换为分布式的 crontab。 这些建议确实都可以在 dcron 之上扩展一层实现。

dxyinme commented 1 year ago

对于第四点,海量定时任务还可以继续优化,例如只缓存未来10min钟内要执行的定时任务,降低内存压力, 是不是需要考虑到存储引擎是否支持,感觉redis和etcd都不是很能支持这种操作 @xiaofeige

xiaofeige commented 1 year ago

应该是实现的问题,这个项目的实现方式我有点不理解,job看起来是没保存的?只是保存了哪些节点可用,哪些节点可以执行定时任务?但是添加定时任务是当前进程执行并保存在当前节点,哪当前节点要是挂了,岂不是定时任务丢了?

我理解设计上应该是每个节点是个时间轮,定时任务和可调度节点都是保存在redis或者etcd中,每个节点定时加载需要执行的定时任务到内存,也就是挂到时间轮上。定时任务过多海量任务就保存在db中,再起个协程定时加载最近10min要执行的任务到缓存。

xiaofeige commented 1 year ago

image

dxyinme commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

xiaofeige commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

对,看代码是没有保存task本身的。

dxyinme commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

对,看代码是没有保存task本身的。

想到了一个保存task的方案: 假设所有的task都是限定好格式的,如下

type Task1 struct {
    Param1 string
    ...
    func Serialize() []byte { ... }
    func Unserialize(v []byte) { ... }
    func Run() {}
}

type Task2 struct {
    Param1 string
    ...
    func Serialize() []byte { ... }
    func Unserialize(v []byte) { ... }
    func Run() {}
}

这样就可以持久化task了,感觉可以在下一阶段的开发中增加持久化task以及task executor restart之后,如何recover task的feature

chengyayu commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

对,看代码是没有保存task本身的。

@libi 是呀,这个做不到高可用的呀。假设 etcd 注册了 A、B、C 三个服务节点,A 节点上的 job 在 B、C 节点上没有呀?如果 A 挂了,A 上的任务久完蛋了。

dxyinme commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

对,看代码是没有保存task本身的。

@libi 是呀,这个做不到高可用的呀。假设 etcd 注册了 A、B、C 三个服务节点,A 节点上的 job 在 B、C 节点上没有呀?如果 A 挂了,A 上的任务久完蛋了。

对于这种情况,一般比较推荐的实现是,注册A1,A2,A3三个节点,跑同样的任务,一个任务在同一时刻只会被某一节点执行;这样挂掉任意节点的时候,就可以保证任务会被其他节点取走执行。对于持久化任务的故障恢复,正在设计并且研究实现的方式。

chengyayu commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

对,看代码是没有保存task本身的。

@libi 是呀,这个做不到高可用的呀。假设 etcd 注册了 A、B、C 三个服务节点,A 节点上的 job 在 B、C 节点上没有呀?如果 A 挂了,A 上的任务久完蛋了。

对于这种情况,一般比较推荐的实现是,注册A1,A2,A3三个节点,跑同样的任务,一个任务在同一时刻只会被某一节点执行;这样挂掉任意节点的时候,就可以保证任务会被其他节点取走执行。对于持久化任务的故障恢复,正在设计并且研究实现的方式。

所以这个库要想保证任务不丢,是不是要在每个节点上都注册上同样的任务呀?

dxyinme commented 1 year ago

我也有点疑问,项目里的所有task都是以golang function的形式存在的,这个应该很难做故障恢复吧。如果是按照真的 crontab的命令行的形式存在的话,这就比较好搞了。

对,看代码是没有保存task本身的。

@libi 是呀,这个做不到高可用的呀。假设 etcd 注册了 A、B、C 三个服务节点,A 节点上的 job 在 B、C 节点上没有呀?如果 A 挂了,A 上的任务久完蛋了。

对于这种情况,一般比较推荐的实现是,注册A1,A2,A3三个节点,跑同样的任务,一个任务在同一时刻只会被某一节点执行;这样挂掉任意节点的时候,就可以保证任务会被其他节点取走执行。对于持久化任务的故障恢复,正在设计并且研究实现的方式。

所以这个库要想保证任务不丢,是不是要在每个节点上都注册上同样的任务呀?

是的,更明确的说法应该是A1,A2,A3三个进程是同一个服务节点A的三个副本

libi commented 1 year ago

一直没注意看到这个issues。。 dcron的架构图在历史commit的readme里有,基本上执行逻辑是所有的 task 是以代码的形式运行在每个副本节点上,但是在执行前会通过存储的副本节点信息和一致性hash来确定哪个节点有权限真正执行。

dxyinme commented 1 year ago

@libi 感觉可以把定时同步哈希环的方式改为更新哈希环之后,trigger各个节点去更新哈希环。这种做法可以用etcd的watch或者redis的pubsub功能来做。可以降低存储的压力。

libi commented 1 year ago

@libi 感觉可以把定时同步哈希环的方式改为更新哈希环之后,trigger各个节点去更新哈希环。这种做法可以用etcd的watch或者redis的pubsub功能来做。可以降低存储的压力。

确实可以,最开始只有redis的实现,所以用的定时刷新,后来加了etcd的实现驱动内部已经是主动更新数据了,当时为了保证驱动一致外层还是定时刷新的逻辑. 可以在驱动接口类增加一个trigger方法注入回调函数 当驱动内数据更新时触发外层更新 数据实时性和性能都会有提升

chengyayu commented 1 year ago

一个正常的单机 cron 代码 + 分布式锁,就可以实现一个支持多节点部署的分布式定时任务组件了。为什么要执着于一致性哈希呢?

dxyinme commented 1 year ago

一个正常的单机 cron 代码 + 分布式锁,就可以实现一个支持多节点部署的分布式定时任务组件了。为什么要执着于一致性哈希呢?

https://github.com/libi/dcron/blob/master/README_CN.md#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E7%9B%B4%E6%8E%A5%E7%94%A8%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0

readme里面有说

chengyayu commented 1 year ago

一个正常的单机 cron 代码 + 分布式锁,就可以实现一个支持多节点部署的分布式定时任务组件了。为什么要执着于一致性哈希呢?

https://github.com/libi/dcron/blob/master/README_CN.md#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E7%9B%B4%E6%8E%A5%E7%94%A8%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0

readme里面有说

(缺陷1)如果任务的执行时间小于系统时间差,任务仍然会被重复执行(某个节点定时执行完毕释放锁,又被另一个因为系统时间之后到达定时时间的节点取得锁)。

(反对意见)这个可以通过分布式锁+业务状态来解决。每个节点执行任务的前提是获取到锁,同时任务状态不是进行中和已完成的。

(缺陷2)即使有极小的误差,因为某个节点的时间会比其他节点靠前,在抢锁时能第一时间取得锁,所以导致的结果是所有任务都只会被该节点执行,无法均匀分布到多节点。

(反对意见)时间不同步可用通过运维手段处理。即便是真的不同步,在分布式定时任务中,对于一个任务来说,最终执行任务的节点只能有一个,这里根本不用考虑负载均衡的问题。只需要保证可用性。即一个节点挂了,其他节点能顶上。

libi commented 1 year ago

一个正常的单机 cron 代码 + 分布式锁,就可以实现一个支持多节点部署的分布式定时任务组件了。为什么要执着于一致性哈希呢?

https://github.com/libi/dcron/blob/master/README_CN.md#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E7%9B%B4%E6%8E%A5%E7%94%A8%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0 readme里面有说

(缺陷1)如果任务的执行时间小于系统时间差,任务仍然会被重复执行(某个节点定时执行完毕释放锁,又被另一个因为系统时间之后到达定时时间的节点取得锁)。

(反对意见)这个可以通过分布式锁+业务状态来解决。每个节点执行任务的前提是获取到锁,同时任务状态不是进行中和已完成的。

(缺陷2)即使有极小的误差,因为某个节点的时间会比其他节点靠前,在抢锁时能第一时间取得锁,所以导致的结果是所有任务都只会被该节点执行,无法均匀分布到多节点。

(反对意见)时间不同步可用通过运维手段处理。即便是真的不同步,在分布式定时任务中,对于一个任务来说,最终执行任务的节点只能有一个,这里根本不用考虑负载均衡的问题。只需要保证可用性。即一个节点挂了,其他节点能顶上。

  1. 首先你没理解这个缺陷的真正含义,其次加了业务状态,直接用业务状态就能保证任务唯一执行,分布式锁都可以去掉了。

  2. 你说的是主备,抛开纯主备时的资源浪费和单机性能极限不谈,有没有可能大家更想要在有主备的同时通过负载均衡更高效的执行任务?

Casper-Mars commented 1 year ago

image

其实按照这里的构思做一个统一调度中心会不会更好?负载均衡、手动触发、流量染色等都可以通过调度中心完成了

dxyinme commented 1 year ago

image

其实按照这里的构思做一个统一调度中心会不会更好?负载均衡、手动触发、流量染色等都可以通过调度中心完成了

可以做,但是业余时间有限,目前还是按照 @libi 的想法先把这个项目作为一个类库的形式存在,这样会更灵活。图里说的东西可以在库的基础上作扩展。

ailose commented 1 year ago

继续加需求,很快就像这个了cronsun项目了: https://github.com/shunfei/cronsun/blob/master/README_ZH.md

Casper-Mars commented 1 year ago

继续加需求,很快就像这个了cronsun项目了: https://github.com/shunfei/cronsun/blob/master/README_ZH.md

看起来像