我的物联网项目(八)简单分布式调度

2024-08-21 11:48

本文主要是介绍我的物联网项目(八)简单分布式调度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

定时调度基本在任何平台或多或少的要用到,实现定时调度的功能很简单,我做过的项目中用到更多的是spring quartz或者spring task,它们在单机上使用定时任务配置是非常简单的,但是在集群环境中就需要面临一个必须解决的问题:如何限定只有一台机器在执行定时任务?

其实spring quartz也可以实现此功能,它是由数据库的数据来确定调度任务是否正在执行, 正在执行则其他服务器就不能去执行该行调度数据,所以需要数据库的11张表来执行此种功能,总的来说成本较高,操作起来也比较复杂。另外一些开源的分布式调度平台也有一些,如当当网的elastic-job,淘宝的TBSchedule,包括阿里云也有SchedulerX,这些分布式调度平台在一定程度上也可以满足集群环境下的功能需求。我当初在技术选型上,只想简单无门槛,不想有太多的学习成本在里面,尤其针对目前阶段的项目,想用一种简单的方式来实现目的就行,所以尽量基于目前的代码和技术。

一 实现思路

主要利用Redis的(Redis用的云集群,暂时不需要考虑单点故障或者不稳定的情况)函数setNX()来实现分布式锁,大概流程是首先是某个集群环境的单边服务器将某一任务标识名(简单来说就是key)作为键存到redis里,并为其设个过期时间,如果这个时候另外的单边服务器也请求过来,先是通过setNX()看看是否能将任务标识名(同一个标识名)插入到redis里,可以的话就返回true,不可以就返回false,如果返回false,说明这次的任务调度别的服务器已经在做了,不需要执行这次任务。如果返回true,说明这次任何调度是由自己来执行。

这个里面由于集群环境下的每台服务器到了时间点都会去执行一遍,当然肯定只有一台才能执行成功,这个里面需要注意两个事情:

  1. 定时调度的策略应该上一个任务完成到下一个任务开始的时间间隔,这样的话才能保证集群环境下其它的服务器下次抢占锁的机率,如spring task的fixedDelay。
  2. 调度时间循环间隔设置当然以具体业务场景为准,但最好算好大概的每次业务执行的时间长短,然后根据这个时间长短来设置定时调度的循环间隔时间。比如说如果小于1s的调用,由于使用redis会有10几毫秒的运算耗费,因此不能保证在1s以下的时间间隔比较均匀。所以尽量保证每台服务器的均匀分布来执行计划任务。
二 代码实现

1.锁对象

public class Lock {private String name;private String value;public Lock(String name, String value) {this.name = name;this.value = value;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}}

2.分布式锁工具类

public class DistributedLockService {private final Logger log = LoggerFactory.getLogger(getClass());private final static long LOCK_EXPIRE = 10;//单个业务持有锁的时间10s,防止死锁private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");@Autowiredprivate RedisTemplate<String, String> redisTemplate;/*** 尝试获取全局锁*/public boolean tryLock(Lock lock) {return getLock(lock,LOCK_EXPIRE);}/*** 操作redis获取全局锁*/public boolean getLock(Lock lock,long lockExpireTime){if (StringUtils.isEmpty(lock.getName()) || StringUtils.isEmpty(lock.getValue())) {return false;}// SETNX成功,则成功获取一个锁if (setNX(lock.getName(), lock.getValue(),lockExpireTime)) {return true;}else {// SETNX失败,说明锁仍然被其他对象保持log.info(lock.getName()+" lock is exist!" + dateFormat.format(new Date()) + "###");return false;}}/*** @Title: setNX * @Description: 设置锁*/private boolean setNX(final String key, final String value, final long expire) {return (Boolean) redisTemplate.execute(new RedisCallback<Boolean>() {@SuppressWarnings("unchecked")public Boolean doInRedis(RedisConnection connection) {byte[] keyBytes = ((RedisSerializer<String>) redisTemplate.getKeySerializer()).serialize(key);boolean locked = connection.setNX(keyBytes, ((RedisSerializer<String>)redisTemplate.getValueSerializer()).serialize(value));if(locked){connection.expire(keyBytes, expire);}return locked;}});}/*** @Title: get * @Description: 根据key获取value*/public Object get(final String key) {return redisTemplate.execute(new RedisCallback<Object>() {@SuppressWarnings("unchecked")public Object doInRedis(RedisConnection connection) throws DataAccessException {byte[] bs = connection.get(((RedisSerializer<String>)redisTemplate.getKeySerializer()).serialize(key));return redisTemplate.getDefaultSerializer().deserialize(bs);}});}/*** 释放锁*/public void releaseLock(Lock lock) {if (!StringUtils.isEmpty(lock.getName())) {redisTemplate.delete(lock.getName());}log.info(lock.getName()+" lock is unchecked!" + dateFormat.format(new Date()) + "###");}}

 

3.定时调度实现

public class ScheduledTasks {@Autowiredprivate DistributedLockService distributedLockService;private final static Logger log= Logger.getLogger(ScheduledTasks.class);private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");//5秒执行一次@Scheduled(fixedDelay = 5000)public void doJob() {log.info("###sync start:"+ dateFormat.format(new Date()) + "###");Lock lock = new Lock("xxlock" , "xxx");if(distributedLockService.tryLock(lock)){log.info("Gets the lock!" + dateFormat.format(new Date()) + "###");       //做具体业务......distributedLockService.releaseLock(lock);}log.info("###sync end:"+ dateFormat.format(new Date()) + "###");}}

 

三 继续优化

上面将做具体业务的代码耦合到了定时调度ScheduledTasks里面,这块需要优化下,后面我们将具体的业务代码单独抽离出来做成一个rest服务,ScheduledTasks里面通过接口请求去执行业务逻辑即可。

定时调度这块后续我们还在继续优化,主要有如下:

1.将调度时间间隔,调度http请求接口,调度的动态开启和关闭,查看目前的调度任务和执行的日志做成后台可视化界面,方便统一管理和运维。

2.集群环境下的服务器做到分片,负载均衡。其实现在的并没有严格做到负载均衡,其实集群环境下每台服务器都在执行,只是没有执行具体业务而已,所以后续这块自己将用代码实现。

这篇关于我的物联网项目(八)简单分布式调度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1093078

相关文章

springboot项目中整合高德地图的实践

《springboot项目中整合高德地图的实践》:本文主要介绍springboot项目中整合高德地图的实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一:高德开放平台的使用二:创建数据库(我是用的是mysql)三:Springboot所需的依赖(根据你的需求再

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

MySQL版本问题导致项目无法启动问题的解决方案

《MySQL版本问题导致项目无法启动问题的解决方案》本文记录了一次因MySQL版本不一致导致项目启动失败的经历,详细解析了连接错误的原因,并提供了两种解决方案:调整连接字符串禁用SSL或统一MySQL... 目录本地项目启动报错报错原因:解决方案第一个:第二种:容器启动mysql的坑两种修改时区的方法:本地

springboot项目中使用JOSN解析库的方法

《springboot项目中使用JOSN解析库的方法》JSON,全程是JavaScriptObjectNotation,是一种轻量级的数据交换格式,本文给大家介绍springboot项目中使用JOSN... 目录一、jsON解析简介二、Spring Boot项目中使用JSON解析1、pom.XML文件引入依

C/C++ chrono简单使用场景示例详解

《C/C++chrono简单使用场景示例详解》:本文主要介绍C/C++chrono简单使用场景示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录chrono使用场景举例1 输出格式化字符串chrono使用场景China编程举例1 输出格式化字符串示

使用vscode搭建pywebview集成vue项目实践

《使用vscode搭建pywebview集成vue项目实践》:本文主要介绍使用vscode搭建pywebview集成vue项目实践,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录环境准备项目源码下载项目说明调试与生成可执行文件核心代码说明总结本节我们使用pythonpywebv

Maven项目中集成数据库文档生成工具的操作步骤

《Maven项目中集成数据库文档生成工具的操作步骤》在Maven项目中,可以通过集成数据库文档生成工具来自动生成数据库文档,本文为大家整理了使用screw-maven-plugin(推荐)的完... 目录1. 添加插件配置到 pom.XML2. 配置数据库信息3. 执行生成命令4. 高级配置选项5. 注意事

eclipse如何运行springboot项目

《eclipse如何运行springboot项目》:本文主要介绍eclipse如何运行springboot项目问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目js录当在eclipse启动spring boot项目时出现问题解决办法1.通过cmd命令行2.在ecl