利用Redisson分布式锁解决多服务器数据刷新问题

利用Redisson分布式锁解决多服务器数据刷新问题

一、业务背景

最近有个需求需要自动刷新网元服务,由于我们生产环境数据库是多台服务器,刷新网元可能导致的数据不一致问题‌,所以采用Redisson分布式锁方式实现这个业务功能。

二、代码实现

1、引入Redisson依赖

xml 复制代码
        <!-- redis -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.7.5</version>
        </dependency>
        <!-- redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.16.0</version>
        </dependency>

2、配置Redisson,实际项目中Redis为集群配置

java 复制代码
@Configuration
public class RedisssionConfig {
    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    /**
     * 对 Redisson 的使用都是通过 RedissonClient 对象
     *
     * @return
     */
    @Bean(name = "redissonClient", destroyMethod = "shutdown") // 服务停止后调用 shutdown 方法
    public RedissonClient redissonClient() {
        // 1、创建配置
        Config config = new Config();

        // 2、集群模式
        // config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        // 根据 Config 创建出 RedissonClient 示例
        config.useSingleServer()
            .setPassword(StringUtils.isEmpty(password) ? null : password)
            .setAddress(host.contains("://") ? "" : "redis://" + host + ":" + port);
        return Redisson.create(config);
    }
}

3、自定义拒绝策略

java 复制代码
@Component
public class RefreshNodeRejectHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 拒绝将任务保存起来
        if (!executor.isShutdown()) {
            saveRejectedTask(r);
        }
    }

    class TaskInfo implements Runnable {
        private final List<NodeVo> nodeVos;

        TaskInfo(List<NodeVo> nodeVos) {
            this.nodeVos = nodeVos;
        }

        @Override
        public void run() {
            System.out.println("执行了TaskInfo的run方法,执行了某某业务逻辑");
        }

        public List<NodeVo> getNodeVos() {
            return nodeVos;
        }
    }

    private void saveRejectedTask(Runnable r) {
        if (r instanceof TaskInfo) {
            TaskInfo taskInfo = (TaskInfo) r;
            List<NodeVo> nodeVos = taskInfo.getNodeVos();
            for (NodeVo node : nodeVos) {
                System.out.println("保存了被拒绝的任务:" + node);
            }
        }
    }
}

4、异步刷新网元服务

java 复制代码
@Service
public class AsynFleshNodeService implements ApplicationRunner {
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private RefreshNodeRejectHandler refreshNodeRejectHandler;

    @Autowired
    private RedissonClient redissionClient;

    @Autowired
    private NodeMapper nodeMapper;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        ThreadPoolExecutor singleThreadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000));
        singleThreadPoolExecutor.execute(this::initTask);
    }

    private void initTask() {
        if (!openFleshNode()) {
            return;
        }

        // 创建线程池,线程队列大小为10000,线程队列满时拒绝任务,拒绝策略为自定义的RefreshNodeRejectHandler
        threadPoolExecutor = new ThreadPoolExecutor(1, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10000),
            Executors.defaultThreadFactory(), refreshNodeRejectHandler);

        while (executeRunningData()) {
            // 如果线程队列大于6000或者线程队列为空且线程数小于等于0,则线程休眠5s
            if (threadPoolExecutor.getQueue().size() >= 6000 || (threadPoolExecutor.getQueue().isEmpty()
                && threadPoolExecutor.getActiveCount() == 0)) {
                try {
                    // 线程休眠5s
                    long sleepTimes = 5000;
                    Thread.sleep(sleepTimes);
                } catch (InterruptedException e) {
                    System.out.println("线程休眠异常");
                }
            }
        }
    }

    private boolean openFleshNode() {
        // 从数据字典获取打开开关
        String isOpen = "Y";
        return StringUtils.equals(isOpen, "Y");
    }

    private boolean paushFleshNode() {
        // 从数据字典获取暂停开关
        String isPause = "N";
        return StringUtils.equals(isPause, "Y");
    }

    // 处理running数据,设置为true代表一直执行的方法
    protected boolean executeRunningData() {
        while (!paushFleshNode() && CollectionUtils.isNotEmpty(nodeMapper.getNodeList())) {
            // 这里锁名建议定义枚举类
            RLock lock = redissionClient.getLock("fleshNode");
            List<NodeVo> nodeVoList = new ArrayList<>();
            try {
                // 设置等待时间10s,锁过期时间60s
                if (!lock.tryLock(10, 60, TimeUnit.SECONDS)) {
                    continue;
                }
                System.out.println("执行刷新网元逻辑,写入数据库");
            } catch (InterruptedException e) {
                System.out.println("获取锁失败");
            } finally {
                // 释放锁
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }

            for (NodeVo nodeVo : nodeVoList) {
                threadPoolExecutor.execute(() -> { System.out.println("执行网元其它操作业务,写入数据库:" + nodeVo); });
            }
        }
        return openFleshNode();
    }
}

三、项目结构及源码

源码下载,欢迎Star: demo-springboot-mybatisplus

相关推荐
-曾牛1 小时前
Java面试:Spring及Spring Cloud技术深度剖析
java·spring·spring cloud·面试·springboot·javaee·面经
素雪风华3 天前
服务容错治理框架resilience4j&sentinel基础应用---微服务的限流/熔断/降级解决方案
java·微服务·sentinel·springboot·服务容错·resilience
南客先生3 天前
SpringBoot、微服务与AI场景题深度解析
微服务·面试·springboot·ai技术
Sunniering4 天前
Springboot整合阿里云腾讯云发送短信验证码 可随时切换短信运营商
java·阿里云·云计算·腾讯云·springboot·短信
mask哥5 天前
实用的java技术架构组件汇总
java·spring·微服务·springboot·vo校验·常用java组件
mask哥6 天前
详解最新链路追踪skywalking框架介绍、架构、环境本地部署&配置、整合微服务springcloudalibaba 、日志收集、自定义链路追踪、告警等
java·spring cloud·架构·gateway·springboot·skywalking·链路追踪
菠萝崽.7 天前
安装docker,在docker上安装mysql,docker上安装nginx
java·mysql·nginx·docker·软件工程·springboot·开发
幽络源小助理7 天前
SpringBoot物资管理系统 | JavaWeb项目设计与实现
java·springboot·javaweb
幽络源小助理8 天前
微信小程序文章管理系统开发实现
java·微信小程序·springboot