美文网首页
利用redis实现集群部署下分布式锁示例分享

利用redis实现集群部署下分布式锁示例分享

作者: haiyong6 | 来源:发表于2021-02-08 12:55 被阅读0次

背景

写了个定时任务,是单机的,但是生产环境往往为了保证程序的稳定性会进行多台机的集群部署,这样定时任务就会出现问题的风险,所以要保证在集群部署下只有一台机的定时任务执行任务,其他机检测到已经有机子在执行任务的时候就不会执行,当执行任务的那台机挂了,其他机可以无缝衔接出一台机接着执行任务,分布式锁就很适合这种场景的应用。

本来想用公司提供的elastic-job框架配合zookeeper来做定时任务,毕竟这个是专门做集群定时的,甚至可以做到把程序中不同定时任务,放在不同机子上同时执行,可以缓解单台服务器的定时压力。但是..我那个项目用到分库分表组件,版本比较新,而公司的elastic-job框架是经过修改过的版本,用的jar包比较老旧,总之搞了半天jar包冲突没能解决,要么我的分库分表jar包降级(极不情愿,之前的代码不是白写了),要么公司的elastic-job升级(不太可能),所以无奈放弃了这个方案。

所以自己用redis的setnx方法实现了一个简单的分布式锁,只要用代码简单控制住永远只有一台机的定时任务在跑就行了。

示例

controller:
用的spring自带的定时任务注解,每十五秒执行一次

package com.ly.mp.iov.controller;

import java.time.LocalDateTime;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import com.ly.mp.iov.service.AppCommentsTaskService;

/**
 * 启辰app评论服务定时任务
 * @author ly-zhaohy
 *
 */
@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class AppCommentsTask {
    private static Logger logger = LoggerFactory.getLogger(AppCommentsTask.class);
    @Autowired
    AppCommentsTaskService appCommentsTaskService;
    //3.添加定时任务
    @Scheduled(cron = "0/15 * * * * ?")
    //或直接指定时间间隔,例如:5秒
    //@Scheduled(fixedRate=5000)
    private void likeCancelLikeTask() {
        //System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
        appCommentsTaskService.likeCancelLikeTask();
    }
}

serviceImpl:
多余的业务代码省略没放上来

package com.ly.mp.iov.service.impl;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ly.mp.iov.common.Constant;
import com.ly.mp.iov.mapper.AppCommentsTaskMapper;
import com.ly.mp.iov.service.AppCommentsTaskService;
import com.ly.mp.jedis.multi.MpJedis;

import jodd.util.StringUtil;
@Service("AppCommentsTaskService")
public class AppCommentsTaskServiceImpl implements AppCommentsTaskService {
    private static Logger logger = LoggerFactory.getLogger(AppCommentsTaskService.class);
    @Autowired
    private MpJedis mpJedis;
    @Autowired
    private AppCommentsTaskMapper appCommentsTaskMapper;
    @Transactional
    public void likeCancelLikeTask() {
        int num = 0;
        String prefix = Constant.REDIS_PREFIX + "likeOrCancelLike";
        String jobIdKey = Constant.REDIS_PREFIX + "jobid";
        String jobId = null == mpJedis.get(jobIdKey) ? "" : mpJedis.get(jobIdKey).toString();
        boolean flag = false;
        
        if(StringUtils.isNotBlank(jobId) && Constant.getJobId().equals(jobId)) {
            flag = true;
        } else if(StringUtils.isBlank(jobId)) {
            long a = mpJedis.setnx(jobIdKey, Constant.getJobId(), 5*60);
            if(a > 0) flag = true;
        }
        
        logger.info("flag====={}",flag);
        if(flag) {
             logger.info("appCommentsTaskBegin..." + LocalDateTime.now());
            try {
                this.getLikeOrCancelLikeFromRedis(prefix, num);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException();
            }
            logger.info("appCommentsTaskEnd..." + LocalDateTime.now());
        }
    }
}

自定义Constant类:

package com.ly.mp.iov.common;

import java.util.UUID;

/**
 * 存放一些公共的变量
 * @author ly-zhaohy
 *
 */
public class Constant {
    public static final String REDIS_PREFIX = "iov:comments:venucia:";
    private static String JOB_ID = "";
    static {
        JOB_ID = UUID.randomUUID().toString().replace("-", "");
    }
    
    public static String getJobId() {
        return JOB_ID;
    }
    
}

Constant类中在程序启动的时候生成一个独一无二不会重复的jobId(uuid)作为程序标识,定时任务启动时把这个jobId放到redis里,当各机子上的定时任务在执行时都要先判断redis里固定键的值有没有值,如果没有值就setnx放入自己的jobId并执行业务代码,如果有值并且值等于自己的jobId也会执行业务代码,如果有值且不等于自己的jobId说明是其他定时程序已经占用了,所以就放弃执行业务代码;这个值在redis里每次放入只有5分钟的有效时间,5分钟之后其他机子就有放进来的机会,如果执行定时任务的那台机子挂了,最多5分钟之后其他机子就可以继续执行定时任务了。如此,就实现了一个简单的分布式锁。

相关文章

  • 利用redis实现集群部署下分布式锁示例分享

    背景 写了个定时任务,是单机的,但是生产环境往往为了保证程序的稳定性会进行多台机的集群部署,这样定时任务就会出现问...

  • 分布式锁

    Redis实现 使用Redis集群实现分布式锁。使用 Redisson 框架,基于LUA脚本去实现 Zookepp...

  • Redis分布式锁

    Redis分布式锁 实现 Redis 锁主要利用 Redis 的 setnx 命令。 加锁命令:SETNX key...

  • Redis分布式锁

    Redis的常用场景 [TOC] ★ Redis分布式锁 示例代码, 其实该分布式锁的实现是存在很多问题.此处仅为...

  • 分布式锁实现

    分布式锁的实现方式 分布式锁的特点 分布式锁的特点: 排他性:保证在分布式部署、服务集群的环境下,共享的资源在同一...

  • Redis 集群规范

    redis手册Redis 集群规范Redis集群安装Redis集群安装 Redis分布式部署,一致性hash;分布...

  • 大佬浅谈分布式锁

    redis 实现 redis 分布锁一、redis 实现分布式锁(可重入锁)redission 实现分布式锁1、对...

  • Zookeeper实现分布式锁(一)While版

    前面文章讲解了用Redis实现分布式锁的方式: 分布式锁之Redis实现(acquire)分布式锁之Redis实现...

  • 基于redis实现的分布式锁

    本文要点 基于redis实现分布式锁demo 基于redis实现分布式锁原理 基于redis实现分布式锁优缺点 正...

  • scrapy分布式爬虫部署-- 爬取知乎用户为例

    环境简介:Ubuntu 环境下 使用MongoDB将数据保存到本地,利用redis-server实现分布式部署使用...

网友评论

      本文标题:利用redis实现集群部署下分布式锁示例分享

      本文链接:https://www.haomeiwen.com/subject/fxvxxltx.html