redis实现简单的分布式任务调度

实现方法:

1、通过zset数据结构实现任务排序,value=任务信息,score=任务触发时间(unix时间戳)

2、ZRANGEBYSCORE task_key -inf  currentTIme,  获取当前时间以前的所有任务 (消费者定时每隔1秒执行一次获取)。

3、通过redis事务或者脚本实现 获取任务后同时删除任务ZREMRANGEBYSCORE task_kety -inf  currentTIme, 解决多台机器重复消费任务的问题

 

value格式自己设计,举例子:  70#eating_job,  获取到value解析为userid=70的用户执行eating_job任务

853b96f09f9ba604920939620a85f1865f0.jpg

 

附上获取任务redis代码(springboot)

public Set<String> zgetAndRemove(String key,Double min,Double max){
        List<Object> txResults = redisTemplate.execute(new SessionCallback<List<Object>>() {
            public List<Object> execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.opsForZSet().rangeByScore(key,min,max);
                operations.opsForZSet().removeRangeByScore(key,min,max);
                return operations.exec();
            }
        });
        Set<String> rs = null;
        if(txResults != null && txResults.size() == 2){
            rs = (Set<String>)txResults.get(0);
        }
        return rs;
    }

 

/**
     * 每秒从redis中获取当前要执行的任务
     */
    @Scheduled(cron = "*/1 * * * * ?")
    private void configureTasks() {
        double min = 0d;
        double max = System.currentTimeMillis() / 1000;
        Set<String> task = redisUtil.zgetAndRemove(GlobalConst.DOG_TIMER_REDIS_KEY,min,max);
        for(String member : task){
            try {
                logger.info("{}准备执行",member);
                String[] data = member.split(GlobalConst.DOG_ACTION_REDIS_MEMBER_SPLIT);
                if (data != null && data.length >= 2) {
                    String uid = data[0];
                    String action = data[1];
                    triggerAction(uid, action);
                }
                else{
                    logger.error("{}发现异常的定时任务数据",member);
                }
            }catch (Exception e){
                logger.error("{}动作触发异常",member);
            }
        }

    }

 

转载于:https://my.oschina.net/u/3783256/blog/3065640

### 使用 Redis 实现分布式定时任务调度 #### 方法概述 为了实现分布式定时任务调度,可以利用 Redis 的特性来确保多个节点之间的同步和一致性。具体来说,可以通过以下几种方式: - **使用 Redis List 或 Set 数据结构** 来存储待执行的任务列表,并通过 Lua 脚本原子性地取出并删除任务[^2]。 - **设置过期时间** 对于每一个即将被执行的任务,在创建时就为其设定一个合理的 TTL (Time To Live),当到达指定的时间后自动触发执行逻辑[^1]。 #### 关键技术点 ##### 定义任务模型 每个任务应该至少包含如下字段: - `id`: 唯一标识符; - `cron_expression` or `fixed_delay/fixed_rate`: 时间表达式或固定延迟/频率; - `status`: 当前状态(等待中、正在运行等); - `next_run_time`: 下次计划执行时刻; ```json { "id": "task_001", "cron_expression": "* * * * *", "status": "pending", "next_run_time": 1678945678 } ``` ##### 存储检索任务 采用 Sorted Set 结构保存所有的任务条目,其中 score 表示下一次预期启动的时间戳。这样就可以很容易找到最接近当前时间的任务来进行处理。 ```bash ZADD tasks_set <timestamp> task_id ZRANGEBYSCORE tasks_set -inf +inf LIMIT 0 1 WITHSCORES ``` ##### 执行任务 编写脚本来周期性检查是否有符合条件的任务需要被激活。一旦发现,则尝试对该任务加锁以防止其他实例重复执行相同的操作。成功获得锁之后再真正调用相应的业务函数完成工作流[^3]。 ```python import redis from datetime import datetime, timedelta import time def acquire_lock(client, lock_name, acquire_timeout=10): identifier = str(uuid.uuid4()) end = time.time() + acquire_timeout while time.time() < end: if client.set(lock_name, identifier, nx=True, ex=end-time.time()): return identifier time.sleep(0.001) raise Exception('Failed to acquire lock') client = redis.Redis(host='localhost', port=6379) while True: now_timestamp = int(time.mktime(datetime.now().timetuple())) next_task = client.zrangebyscore('tasks_set', '-inf', now_timestamp, start=0, num=1, withscores=True)[0] try: acquired = acquire_lock(client, f'task:{next_task[0]}') # Execute the actual job here... release_lock(client, 'task:'+str(next_task), acquired) update_next_execution_time(client, next_task) except IndexError as e: pass # No available jobs at this moment. finally: time.sleep(1) # Wait before checking again. ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值