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

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项目中报错The field screenShot exceeds its maximum permitted size of 1048576 bytes.的问题及解决

《SpringBoot项目中报错ThefieldscreenShotexceedsitsmaximumpermittedsizeof1048576bytes.的问题及解决》这篇文章... 目录项目场景问题描述原因分析解决方案总结项目场景javascript提示:项目相关背景:项目场景:基于Spring

解决Maven项目idea找不到本地仓库jar包问题以及使用mvn install:install-file

《解决Maven项目idea找不到本地仓库jar包问题以及使用mvninstall:install-file》:本文主要介绍解决Maven项目idea找不到本地仓库jar包问题以及使用mvnin... 目录Maven项目idea找不到本地仓库jar包以及使用mvn install:install-file基

springboot项目如何开启https服务

《springboot项目如何开启https服务》:本文主要介绍springboot项目如何开启https服务方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录springboot项目开启https服务1. 生成SSL证书密钥库使用keytool生成自签名证书将

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

Spring Boot项目部署命令java -jar的各种参数及作用详解

《SpringBoot项目部署命令java-jar的各种参数及作用详解》:本文主要介绍SpringBoot项目部署命令java-jar的各种参数及作用的相关资料,包括设置内存大小、垃圾回收... 目录前言一、基础命令结构二、常见的 Java 命令参数1. 设置内存大小2. 配置垃圾回收器3. 配置线程栈大小

Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

《SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能》:本文主要介绍SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能,本文分步骤给大家介绍的... 目录原理解析1. mysql主从复制(Master-Slave Replication)2. 读写分离3.

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Mysql表的简单操作(基本技能)

《Mysql表的简单操作(基本技能)》在数据库中,表的操作主要包括表的创建、查看、修改、删除等,了解如何操作这些表是数据库管理和开发的基本技能,本文给大家介绍Mysql表的简单操作,感兴趣的朋友一起看... 目录3.1 创建表 3.2 查看表结构3.3 修改表3.4 实践案例:修改表在数据库中,表的操作主要