GithubHelp home page GithubHelp logo

libi / dcron Goto Github PK

View Code? Open in Web Editor NEW
418.0 9.0 72.0 371 KB

轻量分布式定时任务库 a lightweight distributed job scheduler library

License: MIT License

Go 100.00%
cron crontab distributed distributed-cron consistent-hashing redis

dcron's People

Contributors

ah-dark avatar coticom avatar cs0604 avatar dependabot[bot] avatar dxyinme avatar jefffeng007 avatar libi avatar thriee avatar xiaojun207 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dcron's Issues

新加入节点可能导致任务重复运行

启动新的节点,如果任务中有小于 10s(defaultDuration) 的定时任务, 可能会导致有的任务会同时在两个节点中运行。

解决方案: 在 dcron.Start() 中至少等待 defaultDuration 时间后再运行 d.cr.Start()。但是此方案可能会导致有的任务停滞运行一段时间。

这叫分布式系统?

目前每个服务节点是有状态的(job 存储),且状态不同步(每个节点内保存的 jobs 不同),并未实现分布式系统的高可用特性。与单机节点的风险无异。

重构driver接口

当前的driver接口有一些冗余设计,现在提出新的设计

type DriverV2 interface {
	Init(serviceName string, timeout time.Duration, logger dlog.Logger)
	GetServiceNodeList() ([]string, error)
	Start() error
}

旧版driver对每个driver的新建,都要使用特定的Conf,这其实是没必要的,我们完全可以只传入对应的client即可。

如此设计的原因:

  1. 对于registerNode/SetXXX等只需要在初始化时候调用的接口,我们使用Init来封装,降低接口复杂度
  2. 减少非必要的重复配置

不支持go1.20 21吗?

github.com/libi/dcron/dlog

vendor/github.com/libi/dcron/dlog/logger.go:8:20: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:12:18: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:17:19: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:18:19: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:19:20: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:27:50: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:34:50: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:38:51: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:42:51: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:50:65: undefined: any
vendor/github.com/libi/dcron/dlog/logger.go:50:65: too many errors
note: module requires Go 1.19

能否发布一个新的release

看issue中对redis 7的支持是2月份增加的,但是releas还是去年的,希望能发布一个新的release,方便go mod引用

怎样删除定时任务?

在运行中有A,B,C三个定时任务,请问怎样删除指定的那个定时,例如要删除B任务怎样删?

leaseRespChan 满了 lease keepalive response queue is full; dropping response send

版本:master 本地测试

leaseRespChan 满了

func (e *EtcdDriver) SetHeartBeat(nodeID string) {
	leaseID, err := e.putKeyWithLease(nodeID, nodeID)
	if err != nil {
		log.Printf("putKeyWithLease error: %v", err)
		return
	}

	leaseRespChan, err := e.cli.KeepAlive(context.Background(), leaseID)

	if err != nil {
		log.Printf("keepalive error:%v", err)
		return
	}

         // 尝试修复
	go func (){
		for {
			select {
			case resp := <-leaseRespChan:
				if resp == nil {
					log.Printf("ectd cli keepalive unexpected nil")
				}
			case <-time.After(businessTimeout):
				log.Printf("ectd cli keepalive timeout")
			}
		}
	} ()
}

建议将Duration放到可配置项中

建议将Duration放到可配置项中,不然
func (np *NodePool) tickerUpdatePool() { tickers := time.NewTicker(time.Second * defaultDuration) for range tickers.C { if np.dcron.isRun { np.updatePool() } } }
直接for循环扫描redis,对性能有影响.

如何保证一个任务在同一个周期内只执行一次?

为什么不直接用分布式锁实现?
通过各个节点在定时任务内抢锁方式实现,需要依赖各个节点系统时间完全一致,当系统时间有误差时可能会导致以下问题:

如果任务的执行时间小于系统时间差,任务仍然会被重复执行(某个节点定时执行完毕释放锁,又被另一个因为系统时间之后到达定时时间的节点取得锁)。
即使有极小的误差,因为某个节点的时间会比其他节点靠前,在抢锁时能第一时间取得锁,所以导致的结果是所有任务都只会被该节点执行,无法均匀分布到多节点。

dcron 是如何解决这个问题的呢,我看核心代码:
通过一致性hash 获取到的节点为当前节点的话即进行执行,好像也会出现时间误差的情况吧

//Run is run job
func (job JobWarpper) Run() {
	//如果该任务分配给了这个节点 则允许执行
	if job.Dcron.allowThisNodeRun(job.Name) {
		if job.Func != nil {
			job.Func()
		}
		if job.Job != nil {
			job.Job.Run()
		}
	}
}

stop优化

cron的stop是有返回一个context,可以判断,context.Done正在执行的任务是否都完成了
Dcron的stop忽略了这个参数,应该优化一下,支持等待任务完成

The cron tab will stop after a while.

If two machines start the cron tab at the same time, the cron tab will stop after a while, the code is as follows

func main() {
drv, _ := dredis.NewDriver(&dredis.Conf{
Host: "10.203.169.19",
Port: 1841,
Password: "005f4ce0b221abf",
}, redis.DialConnectTimeout(time.Second*10))
dcronDemo := dcron.NewDcron("server1", drv, cron.WithSeconds())
//添加多个任务 启动多个节点时 任务会均匀分配给各个节点

dcronDemo.AddFunc("s1 test1", "0 */1 * * * *", func() {
	fmt.Println("执行 service1 test1 任务", time.Now().Format("15:04:05"))
})
dcronDemo.AddFunc("s1 test2", "0 */1 * * * *", func() {
	fmt.Println("执行 service1 test2 任务", time.Now().Format("15:04:05"))
})


dcronDemo.AddFunc("s1 test3", "0 */1 * * * *", func() {
	fmt.Println("执行 service1 test3 任务", time.Now().Format("15:04:05"))
})
dcronDemo.Start()


//测试120分钟后退出
time.Sleep(120 * time.Minute)

}

集群稳定性维护

在集群增减节点,哈希环同步时间中执行的任务可能会丢失或者被重复执行,这个可能需要有一个可行的稳定性维护方案。

负载均衡怎么配置

你好,启动了多个实例在不同种类的机器上运行
image
image
image
image
当停掉某个节点,会自动切换,这是没问题的,但是如果各个节点都稳定运行时,怎么做到负载均衡?

数据竞争bug

Dcron.Start() //启动分布式定时任务
Dcron.Stop()//停止定时任务

会产生数据竞争

代码
`
//分布式定时任务路由
Dcron.Start() //启动分布式定时任务

quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
sign := <-quit //阻塞等待结束信号
utils.Logger().Info().Msgf("%v %v %v %+v", "SERVER_RUN", "server_close_sig", "", sign)

//停止定时任务
Dcron.Stop()

==================
WARNING: DATA RACE
Read at 0x00c000594038 by goroutine 46:
github.com/libi/dcron.(*NodePool).tickerUpdatePool()
C:/Users/yunwe/go/pkg/mod/github.com/libi/[email protected]/node_pool.go:77 +0xfb
github.com/libi/dcron.(*NodePool).StartPool.func1()
C:/Users/yunwe/go/pkg/mod/github.com/libi/[email protected]/node_pool.go:57 +0x39

Previous write at 0x00c000594038 by main goroutine:
github.com/libi/dcron.(*Dcron).Stop()
C:/Users/yunwe/go/pkg/mod/github.com/libi/[email protected]/dcron.go:159 +0x39
main.main()
D:/code_qifan/jump_live/main.go:81 +0x486

Goroutine 46 (running) created at:
github.com/libi/dcron.(*NodePool).StartPool()
C:/Users/yunwe/go/pkg/mod/github.com/libi/[email protected]/node_pool.go:57 +0x1e5
github.com/libi/dcron.(*Dcron).Start()
C:/Users/yunwe/go/pkg/mod/github.com/libi/[email protected]/dcron.go:134 +0x84
main.main()
D:/code_qifan/jump_live/main.go:69 +0x271

`

start dcron error

我的代码:
drvRedis, _ := redis.NewDriver(&redis.Conf{
Addr: conf.RedisSetting.RedisHost,
Password: conf.RedisSetting.RedisPassword,
//Wait: true,
})
if err := drvRedis.Ping(); err != nil {
log.Fatal(err.Error())
return
}
log.Println("init redis ======")
dCronEngine := dcron.NewDcron("cron_server1", drvRedis, cron.WithSeconds())

tg := new(product_platform.TaskGroup)
//每小时执行一次
//dcr.AddFunc("TestTask", "0 0 * * * *", tg.TestTask)
dCronEngine.AddFunc("GetBipData", "0 0 * * * *", tg.GetBipData)
dCronEngine.AddFunc("GetTeaData", "0 0 * * * *", tg.GetTeaData)
dCronEngine.Start()

返回的错误日志:
2022-05-27 20:55:06.386 INFO runtime/proc.go:255 init redis ======
[dcron] 2022/05/27 20:55:06 INFO: addJob 'GetBipData' : 0 0 * * * *
[dcron] 2022/05/27 20:55:06 INFO: addJob 'GetTeaData' : 0 0 * * * *
[dcron] 2022/05/27 20:55:06 ERR: dcron start node pool error ERR unkown command or protocal error

部署到linux服务器上就出现错误了,本地没有什么事,请问这是什么问题,执行到start方法的时候就出错了?

新增Driver:仅靠节点间通信同步状态

大部分服务节点都部署在同一子网内,节点间通信不需要经过多级路由转换,通信效率会非常高。
所以可以考虑直接通过节点间通信来同步状态,大概思路如下:

  1. 初始化driver时,传入所有节点ip:port列表。
  2. 服务启动阶段每个节点依次与其他节点建立 rpc 连接,连接成功后当前节点保存所有节点状态。
  3. Job 执行判定阶段,直接通过 rpc 获取目前所有节点状态,如状态列表与上次不一致则重建 hash 环。
  4. 一致性hash选举执行节点。

这么做的优势在于可以去掉存储依赖,并且状态同步将更精准,不会产生心跳周期导致的瞬间状态不一致情况。

当然如果存在节点间跨机房等情况,不适用该 driver。

添加bash支持

@libi
环境变量设定支持 / 命令宏替换支持就不考虑了,不适合dcron

可以 补充一下bash命令支持


func (d *Dcron) AddCmds(jobName, cronStr string, cmds []string) (err error) 

//JobWarpper is a job warpper
type JobWarpper struct {
	ID      cron.EntryID
	Dcron   *Dcron
	Name    string
	CronStr string
	Func    func()
	Job     Job

       Type      string // command, http, rpc etc. 任务类型
        Commands []string // 要执行的shell命令
       // 用于存储分隔后的任务
	cmd     []string
}



func (job *JobWarpper) splitCmd(i int) {
	job.cmd = strings.SplitN(job.Command[i], " ", 2)
}

func (job *JobWarpper) CmdRun() {
	var cmd *exec.Cmd
	var stdOut bytes.Buffer
	var stdErr bytes.Buffer
	var cmdStr string
	for i := range job.Commands {
		job.splitCmd(i)

		// 超时控制
		c := strings.Join(job.cmd, " ")
		fmt.Println(c)
		if job.Timeout > 0 {
			ctx, cancel := context.WithTimeout(context.Background(), time.Duration(job.Timeout)*time.Second)
			defer cancel()
			cmd = exec.CommandContext(ctx, "sh", "-c", c)
		} else {
			cmd = exec.Command("sh", "-c", c)
		}

		//cmd.SysProcAttr = sysProcAttr
		cmd.Stdout = &stdOut
		cmd.Stderr = &stdErr

		if i > 0 {
			cmdStr = cmdStr + "\n" + c
		} else {
			cmdStr = c
		}
		if err := cmd.Start(); err != nil {
			// 错误处理
			break
		}

		if err := cmd.Wait(); err != nil {
			// 错误处理
			if p, _ := os.FindProcess(cmd.Process.Pid); p != nil {
				// 无法杀死任务启动的子进程
				p.Kill()
			}
			// 告警之类
			break
		}
	}
	result := "------ " + cmdStr + " ----\n" + stdOut.String() + " ----\n" + stdErr.String()

	fmt.Println(result)
}

顺便提一嘴,可以加一下系统信号监控

func (np *NodePool) monitorExit() {
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
	<-quit

	// 一些退出前  要做的节点处理逻辑 如关闭redis 连接 通知其他节点我退出了==
}

无法打印输出cron的全量日志

问题说明:
当设置的标准日志输出,在addFunc中的func想要日志输出,实际上并没有输出日志。设置WithPrintLogInfo只是打印了dcron的日志,cron的日志还是无法打印。
问题原因:
默认设置日志格式的时候

// WithLogger both set dcron and cron logger.
func WithLogger(logger interface{ Printf(string, ...interface{}) }) Option {
	return func(dcron *Dcron) {
		//set dcron logger
		dcron.logger = logger
		//set cron logger,这里设置的是标准日志输出,很多信息无法打印
		f := cron.WithLogger(cron.PrintfLogger(logger))
		dcron.crOptions = append(dcron.crOptions, f)
	}
}
// PrintfLogger wraps a Printf-based logger (such as the standard library "log")
// into an implementation of the Logger interface which logs errors only.
func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
	return printfLogger{l, false}
}

// VerbosePrintfLogger wraps a Printf-based logger (such as the standard library
// "log") into an implementation of the Logger interface which logs everything.
func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
	return printfLogger{l, true}
}

建议设置dcron.loginfo设置为true的时候,则cron改成,f := cron.VerbosePrintfLogger(cron.PrintfLogger(logger)) ?

提交PR建议

需要提交新feature可以在develop分支的基础上进行开发,然后提交到develop分支;
需要提交bugfix可以在master分支的基础上进行开发,经过完整测试之后和入master,保证master分支的稳定性。

一秒执行redis命令200多次?这有点太多了吧,我设置的是秒级定时任务

1676531774.933469 [0 127.0.0.1:57462] "scan" "1759" "match" "distributed-cron:mole_show:"
1676531774.933515 [0 127.0.0.1:57462] "scan" "2623" "match" "distributed-cron:mole_show:
"
1676531774.933555 [0 127.0.0.1:57462] "scan" "127" "match" "distributed-cron:mole_show:"
1676531774.933595 [0 127.0.0.1:57462] "scan" "2815" "match" "distributed-cron:mole_show:
"

一秒执行redis命令200多次?这有点太多了吧,我设置的是秒级定时任务

可以考虑包内实现定时任务库

可以考虑将 robfig/cron 完全内置:
对内可以实现更精准的任务执行时机判断。
对外用户调用接口将与robfig/cron完全一致,只通过替换包名即可无缝替换单机模式。 如果需要分布式支持,只需要配置额外的 option 选项开启分布式。

例如开启某个定时任务:
cron 用法

 c := cron.New(cron.WithSeconds()) 
 c.AddJob("* * * * * ?", job)
 c.Start()

dcron用法

 c := dcron.New(dcron.WithSeconds()) 
 c.AddJob("* * * * * ?", job)
 c.Start()

如需开启分布式时:

 c := dcron.New(dcron.WithSeconds(),dcron.WithDistributed(dcron.Driver)) 
 c.AddJob("* * * * * ?", job)
 c.Start()

关于负载均衡

我想问下:
文档中提到 "负载均衡:根据任务数据和节点数据均衡分发任务。",

  1. 负载均衡是使用一致性Hash做到的么?
  2. 根据任务数据和节点数据,这是怎么做到的?,在代码中我并没有看到

需求想法沟通

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

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

任务不会平均分配

本地启动三个实例,会一直在其中一个实例运行,不会分配到其他实例

go.opentelemetry.io/otel/metric/sdkapi: module go.opentelemetry.io/otel/metric@latest found (v1.19.0), but does not contain package go.opentelemetry.io/otel/metric/sdkapi

dcron在和gorm/gen一起使用的时候
go.mod

module dcron-test

go 1.21

require (
	github.com/go-redis/redis/v8 v8.11.5
	github.com/libi/dcron v0.5.1
	gorm.io/gen v0.3.23
)
 go mod tidy
go: finding module for package go.opentelemetry.io/otel/internal/metric
go: finding module for package go.opentelemetry.io/otel/semconv
go: finding module for package go.opentelemetry.io/otel/unit   
go: found go.opentelemetry.io/otel/internal/metric in go.opentelemetry.io/otel/internal/metric v0.27.0
go: finding module for package go.opentelemetry.io/otel/metric/registry
go: finding module for package go.opentelemetry.io/otel/semconv
go: dcron-test/jobx imports                                                                                                                                    
        github.com/libi/dcron tested by                                                                                                                        
        github.com/libi/dcron.test imports                                                                                                                     
        go.etcd.io/etcd/tests/v3/integration imports                                                                                                           
        go.etcd.io/etcd/server/v3/embed imports                                                                                                                
        go.opentelemetry.io/otel/semconv: module go.opentelemetry.io/otel@latest found (v1.19.0), but does not contain package go.opentelemetry.io/otel/semconv
go: dcron-test/jobx imports
        github.com/libi/dcron tested by
        github.com/libi/dcron.test imports
        go.etcd.io/etcd/tests/v3/integration imports
        go.etcd.io/etcd/server/v3/embed imports
        go.opentelemetry.io/otel/exporters/otlp imports
        go.opentelemetry.io/otel/sdk/metric/controller/basic imports
        go.opentelemetry.io/otel/metric/registry: module go.opentelemetry.io/otel/metric@latest found (v1.19.0), but does not contain package go.opentelemetry.io/otel/metric/registry

有啥办法解决吗

Dcron开发背景

背景

项目中的定时任务越来越多,为了防止任务重复执行曾经使用过的方案:

  • 只启用了一个节点。
  • 固定循环间隔,使用分布式事务锁。

第一种方案没有容错机制,当单个节点宕机,所有定时任务都无法正常执行。

第二种方案不能跟cron一样灵活设定时间,比如需要设定每天1点执行就必须借助数据库或者其他存储手段去轮询,非常低效。

在对比了市面上主流的分布式定时任务库后,发现要不就是过重,要不就是使用复杂或者不能使用golang无缝接入。所以萌生了开发一个分布式定时任务库。

要解决的痛点主要包括:

  1. 高一致可靠性
  2. 能复用现在架构内的存储系统,redis或者mysql。
  3. 引入足够方便,使用方法足够简单。

原理

在参考了gojob源码后想到了全新的解决思路:

将所有节点存入公共存储(目前基本所有项目都使用redis作为缓存库,所以首先开发了redis支持)后使用一致性hash算法来选举出执行单个任务的节点来保证唯一性,所有节点都按照写入的cron预执行,在任务执行入口处根据一致性hash算法来判断该任务是否应该由当前节点执行。

ERR: node pool is empty

最近一直遇到报错:[DCron]2023/11/29 04:38:00 ERR: node pool is empty;

使用的版本

	github.com/libi/dcron v0.2.2
	github.com/robfig/cron/v3 v3.0.1
[root@dev-node1-ops1 log]# go version
go version go1.19.10 linux/amd64

初始化和相关代码,分钟级别的cron有50+,秒级的cron有一个:

`drv, _ := redis.NewDriver(&redis.Conf{
Host: g.Config().RedisStandalone.Host,
Port: g.Config().RedisStandalone.Port,
})

logger := log.New(os.Stdout, "[DCron]", log.LstdFlags)
rec := dcron.CronOptionChain(cron.Recover(cron.PrintfLogger(logger)))
dc := dcron.NewDcronWithOption("HbsCron", drv, rec, dcron.WithLogger(logger), dcron.WithHashReplicas(10), dcron.WithNodeUpdateDuration(time.Second*10))

dc.AddFunc("cache.WarmUPForCMDB()", g.Config().DCron.WarmUPForCMDB, func() {
	fmt.Println("DCron execute task: cache.WarmUPForCMDB()", time.Now().Format("15:04:05"))
	cache.WarmUPForCMDB()
})

dc.AddFunc("cache.LoopInit()", g.Config().DCron.LoopInit, func() {
	fmt.Println("DCron execute task: cache.LoopInit()", time.Now().Format("15:04:05"))
	cache.MonitoredHosts.Init()
	cache.MonitoredHosts.CorrectInitHost()
})

......
dc.Start()

secondLevelCron := dcron.NewDcron("secondLevelCron", drv, cron.WithSeconds())
secondLevelCron.AddFunc("cache.DeliveryTimeoutCheck()", g.Config().DCron.DeliveryTimeoutCheck, func() {
fmt.Println("DCron execute secondLevelCron task: cache.DeliveryTimeoutCheck()", time.Now().Format("15:04:05"))
cmd_tunnel.DeliveryTimeoutCheck()
})

secondLevelCron.Start()

`

具体报错

Nov 29 04:38:00 dev-node1-ops1 hbs: DCron execute secondLevelCron task: cache.DeliveryTimeoutCheck() 04:38:00
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 INFO: job 'cache.AutoAddStatusForMaintenance()' running in node
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 ERR: node pool is empty
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 INFO: job 'cache.PopulateWorkersByTeamManagers()' running in node
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 ERR: node pool is empty
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 INFO: job 'cache.SyncSpecifiedRecords()' running in node
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 INFO: job 'cache.SetStaleSysProperty()' running in node
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 ERR: node pool is empty
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 ERR: node pool is empty
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 INFO: job 'cache.CallIncident()' running in node
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 ERR: node pool is empty
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 INFO: job 'cache.GetCache2mongo()' running in node
Nov 29 04:38:00 dev-node1-ops1 hbs: [DCron]2023/11/29 04:38:00 ERR: node pool is empty

关于批量添加

从mysql批量加入定时,是加一个for然后
for (⋯⋯) {
dcron.AddFunc("test n+1","*/3 * * * *",func(){
fmt.Println("执行 test n+1 任务",time.Now().Format("15:04:05"))
})
}
这样子添加吗?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.