基于quartz实现定时任务管理系统
基于quartz实现定时任务管理系统
背景
说起定时任务框架,首先想到的是Quartz。这是定时任务的老牌框架了,它的优缺点都很明显。借助PowerJob 的readme文档的内容简单带过一下这部分。

除了上面提到,还有elastic-job-lite、quartzui也是相当火热的,可以自行了解一下。
那么都这么多后起之秀,个个吊打quartz,为什么我还选择用quartz来做定时任务?技术是服务于业务的,肯定是有需求呗。公司有个统一管理平台,现在需要开发一个定时任务管理平台,可以动态去管理配置定时任务、查看运行日志。
为什么不考虑其它的框架?由于需求要定制化ui界面和定时任务执行结果接入统一的通知中心等等需求,上面大多数框架都是通过简单配置开箱即用,定制化需要对源码有一定熟悉程度,而quartz我在大学就用了很多次了,非常熟悉,改造相对容易。
需求分析
- 定时任务的增删改查
- 定时任务执行日志查看
详细设计
开发环境
jdk 1.8
spring boot 2.7.7
quartz 单机版
Mysql 8.0
1. 定时任务的实现
quartz的任务实际是通过内置Java类实现job接口或者继承QuartzJobBean实现executeInternal来实现定时任务的添加。所以我们在管理页面上所做的增删改查操作都不可能是真正意义上地去修改了任务类,而是修改了映射类。举个例子来说,比如Power Job的管理界面

我们修改的这些任务名称、定时信息的持久化操作都不会是操作了真正的任务类,而是修改了一个绑定这个任务类的映射类。如下图中的类就是任务类

而图片中的这些就是属性就是映射类的属性。
你可以先是觉得任务类和映射类之间是一对一的关系,映射类记录了定时任务的执行频率(cron)、名称、任务类完整类名等其它的信息,然后在执行过程中通过完整类名反射获得任务类,任务类再根据这些信息去执行。
但如果每个定时任务,我们都要去实现Job接口创建一个类,相同的那些代码比如获取trigger、scheduler等等,都要出现在每个类中,每次添加一个定时任务都要多一个专门的定时任务类。每次开发时都要关注业务和定时任务类之间的关系,多了之后是有点烦。
我推荐的做法是,创建一个具备http请求功能的任务类,将业务操作开发成一个接口,在配置映射类时,将http链接配置上去,这样一到时间 ,就会请求到对应的业务接口。这样使得后续的定时任务功能开发更专注于业务开发,方便使用。尤其是团队开发中,对一些不熟悉quartz的朋友格外友好。
编码实现
引入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.11</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
映射实体类
@TableName
@Data
public class ScheduleJob {@TableIdprivate Long id;@TableFieldprivate String jobName;@TableFieldprivate String cronExpr; // cron表达式@TableFieldprivate String jobStatus; // 任务状态@TableFieldprivate String url; // 业务接口
}
必不可少的属性大概就是上面这几个,后续需要再添加
service层与mapper层我偷个懒,直接用mybatisplus的api
ScheduleJobService
@Service
public class ScheduleJobService extends ServiceImpl<ScheduleJobMapper, ScheduleJob> {
}
ScheduleJobMapper
@Mapper
public interface ScheduleJobMapper extends BaseMapper<ScheduleJob> {
}
有了这些就可以先做个添加定时任务的接口测试一下流程是否通畅
ScheduleJobController
@RestController
public class ScheduleJobController {@Resourceprivate ScheduleJobService scheduleJobService;@PostMapping("/scheduleJob")public ResultBean createScheduleJob(@RequestBody ScheduleJob scheduleJob) {scheduleJobService.save(scheduleJob);return ResultBean.SUCCESS();}}
这个ResultBean是我封装的一个返回对象
@Data
@AllArgsConstructor
public class ResultBean {private String code;private String msg;private Object data;public static ResultBean SUCCESS(Object... data) {String code = "200";String msg = "操作成功!";return new ResultBean(code, msg, data);}
}
简单测试了一下,能写入库

目前为止的操作和定时任务还没有一分钱关系,接下来我们来接入定时任务。
创建任务类,这个任务类要支持发送http请求,所以取名为RestRequestJob,不过我们还不知道能不能真的按着指定的时间的执行,先不写太复杂。
@Slf4j
public class RestRequestJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {log.info("hello quartz");}
}
映射实体类有了,任务类也有了,就差任务类怎么按照映射实体类的信息去执行定时任务了
@Service
public class ScheduleJobManageService {@Autowiredprivate ScheduleJobService scheduleJobService;@Autowiredprivate Scheduler scheduler;public void createScheduleJob(ScheduleJob scheduleJob) {JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName() + scheduleJob.getId());JobDetail jobDetail = JobBuilder.newJob(RestRequestJob.class).withIdentity(jobKey).build();// 表达式调度构建器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpr());// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(TriggerKey.triggerKey(scheduleJob.getId().toString())).withSchedule(cronScheduleBuilder).build();jobDetail.getJobDataMap().put("TASK_PROPERTIES", scheduleJob);try {scheduler.scheduleJob(jobDetail, trigger);if (scheduleJob.getJobStatus().equals("0")) {scheduler.pauseJob(jobKey);}} catch (SchedulerException e) {throw new RuntimeException(e);}}}
jobKey是定时任务的唯一标识
jobDetail是任务类
trigger是执行时间
jobDetail.getJobDataMap().put("TASK_PROPERTIES", scheduleJob);是把目前的映射类信息保存到缓存中,可提供给其它地方用。
scheduler.scheduleJob是开始定时任务的线程
scheduler.pauseJob如果发现状态是暂停的话,则暂停定时任务
回到ScheduleJobController类的createScheduleJob方法,调用scheduleJobManageService类中的创建定时任务方法
@PostMapping("/scheduleJob")public ResultBean createScheduleJob(@RequestBody ScheduleJob scheduleJob) {scheduleJobService.save(scheduleJob);scheduleJobManageService.createScheduleJob(scheduleJob);return ResultBean.SUCCESS();}
到此再来测试一下,添加定时任务时,能不能启动任务类

定时任务启动成功了,但是目前除了停止应用,我们完全没办法能让它停下来。得写个暂停方法
@RestController
public class ScheduleJobManagerController {@Resourceprivate ScheduleJobManageService scheduleJobManageService;@GetMapping("/scheduleJobManage/changeStatus/{id}")public ResultBean changeStatus(@PathVariable("id") Long id) {try {scheduleJobManageService.changeStatus(id);return ResultBean.SUCCESS();} catch (SchedulerException e) {throw new RuntimeException(e);}}
}
在ScheduleJobManageService类中添加changeStatus方法
@Transactionalpublic void changeStatus(Long id) throws SchedulerException {ScheduleJob scheduleJob = scheduleJobService.getById(id);String status = scheduleJob.getJobStatus().equals("1") ? "0" : "1";scheduleJob.setJobStatus(status);scheduleJobService.saveOrUpdate(scheduleJob);JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName() + scheduleJob.getId());switch (status) {case "1":log.info("已启动任务"+scheduleJob.getId());scheduler.resumeJob(jobKey);break;case "0":log.info("已暂停任务"+scheduleJob.getId());scheduler.pauseJob(jobKey);break;}}
测试一下,暂停启动都能成功

那我们这些定时任务遭遇了服务器重启之后都没法再启动了吗?那肯定不是,我们在服务启动过后再加载回来这些定时任务不就ok了嘛。在ScheduleJobManageService类中加多一个init方法
@PostConstructpublic void init() throws SchedulerException{scheduler.clear();List<ScheduleJob> jobs = scheduleJobService.list();jobs.parallelStream().forEach(this::createScheduleJob);}
一启动之后,那堆没暂停的定时任务一直在跑

现在该实现http请求了,回到RestRequestJob类的executeInternal
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// log.info("hello quartz");ScheduleJob scheduleJob = new ScheduleJob();BeanUtils.copyProperties(context.getMergedJobDataMap().get("TASK_PROPERTIES"), scheduleJob);log.info("calling...{}",scheduleJob.getUrl());String s = HttpUtil.get(scheduleJob.getUrl());log.info(s);}
context.getMergedJobDataMap().get("TASK_PROPERTIES")这个就是刚才存进去的scheduleJob对象,这样就可以将url取出来,去访问了。
我做了一个很简单的接口用于测试是否url可以访问通的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UEO95tVV-1678082698242)(image-20230303154257116.png)]
调用成功
目前还缺定时任务修改、移除,执行日志记录
定时任务修改
ScheduleJobController
@PutMapping("/scheduleJob")public ResultBean updateScheduleJob(@RequestBody ScheduleJob scheduleJob) {try {scheduleJobManageService.updateScheduleJob(scheduleJob);return ResultBean.SUCCESS();} catch (SchedulerException e) {throw new RuntimeException(e);}}
ScheduleJobManageService
@Transactionalpublic void updateScheduleJob(ScheduleJob scheduleJob) throws SchedulerException {ScheduleJob oldInfo = scheduleJobService.getById(scheduleJob.getId());scheduleJobService.saveOrUpdate(scheduleJob);if (!oldInfo.getCronExpr().equals(scheduleJob.getCronExpr()) || !oldInfo.getUrl().equals(scheduleJob.getUrl())) {JobKey jobKey = JobKey.jobKey(scheduleJob.getId().toString());if (scheduler.checkExists(jobKey)){// 防止创建时存在数据问题 先移除,然后在执行创建操作scheduler.deleteJob(jobKey);}createScheduleJob(scheduleJob);}log.info("更新成功!");}
删除定时任务
ScheduleJobController
@DeleteMapping("/scheduleJob/{id}")public ResultBean deleteScheduleJob(@PathVariable Long id) {try {scheduleJobManageService.deleteScheduleJob(id);return ResultBean.SUCCESS();} catch (SchedulerException e) {throw new RuntimeException(e);}}
ScheduleJobManageService
@Transactionalpublic void deleteScheduleJob(Long id) throws SchedulerException {boolean b = scheduleJobService.removeById(id);if (b) {scheduler.deleteJob(JobKey.jobKey(id.toString()));}}
2. 日志写入
实体类
@Data
@TableName
public class ScheduleJobLog {@TableId(type = IdType.AUTO)private Long id;@TableFieldprivate Long schdJobId;@TableFieldprivate Date startTime;@TableFieldprivate Date endTime;@TableFieldprivate Integer executeRes;@TableFieldprivate String errorInfo;
}
Mapper和Service层都是用了mybatisplus偷工减料,就不累赘了。
重点是,我们的日志怎么切入?切入在什么地方?quartz实际上当trigger时间到了的时候,他去执行Job的实现类。也就是说我们的日志应该切入在RestRequestJob类中,那接下来改装一下executeInternal方法
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// log.info("hello quartz");ScheduleJobLog scheduleJobLog = new ScheduleJobLog();ScheduleJob scheduleJob = null;try {scheduleJobLog.setStartTime(new Date());scheduleJob = new ScheduleJob();BeanUtils.copyProperties(context.getMergedJobDataMap().get("TASK_PROPERTIES"), scheduleJob);log.info("calling...{}",scheduleJob.getUrl());
// String s = HttpUtil.get(scheduleJob.getUrl());HttpResponse httpRes = HttpRequest.get(scheduleJob.getUrl()).execute();log.info(String.valueOf(httpRes.getStatus()));if (httpRes.getStatus() == HttpStatus.HTTP_NOT_FOUND) {throw new Exception(String.valueOf(HttpStatus.HTTP_NOT_FOUND));}scheduleJobLog.setExecuteRes(1); // 执行成功} catch (Exception e) {scheduleJobLog.setExecuteRes(-1); // 执行失败scheduleJobLog.setErrorInfo(e.getMessage());throw new RuntimeException(e);} finally {scheduleJobLog.setEndTime(new Date());scheduleJobLog.setSchdJobId(scheduleJob.getId());ScheduleJobLogService logService = SpringUtil.getBean(ScheduleJobLogService.class);logService.save(scheduleJobLog);}}
SpringUtil.getBean是使用了hutool的工具,目的是在不被spring管理的类中也能拿到Bean。
到这里看起来貌似没什么问题,能增删改定时任务了,日志也能插入了。但是经验告诉我,如果碰到那种定时任务需要同步几万数据入库的,http的长连接也扛不住,而且一直在等待也对性能造成极大的影响。
那么能不能说,发出去的请求,到对应业务接口自己执行得了,不管你的死活,你执行完之后告诉我一声执行结果得了。这就是异步思想。当http请求过来的时候,任务类中把执行的日志中的id也带过去,业务接口这边一旦收到,马上就返回结果,告诉任务类这边,收到执行指令,你安就得了。任务类把执行日志的状态设置为【执行中】。业务接口执行结束之后调用一下,定时任务日志的接口,把状态更新即可。
- 新增更新执行状态接口
@RestController
public class ScheduleJobLogController {@Autowiredprivate ScheduleJobLogService logService;@PutMapping("/scheduleJobLog")public ResultBean updateStatus (@RequestBody ScheduleJobLog scheduleJobLog) {logService.updateStatus(scheduleJobLog);return ResultBean.SUCCESS();}
}
2.实现更新状态方法updateStatus
@Service
public class ScheduleJobLogService extends ServiceImpl<ScheduleJobLogMapper, ScheduleJobLog> {public void updateStatus(ScheduleJobLog scheduleJobLog) {ScheduleJobLog jobLog = this.getById(scheduleJobLog.getId());jobLog.setExecuteRes(scheduleJobLog.getExecuteRes());jobLog.setErrorInfo(scheduleJobLog.getErrorInfo());jobLog.setEndTime(new Date()); // 更新执行结束时间this.saveOrUpdate(jobLog);}}
在业务执行接口上有比较大的变动
@RestController
public class ExecuteJobController {@GetMapping("/scheduleJob/helloWorld")public ResultBean sayHelloWorld(Long logId) {ScheduleJobLog jobLog = new ScheduleJobLog();jobLog.setId(logId);new Thread(() -> {try {// 模拟超时长业务
// Thread.sleep(2000);jobLog.setExecuteRes(1);//throw new Exception("just test");} catch (Exception e) {jobLog.setExecuteRes(-1);jobLog.setErrorInfo(e.getMessage());throw new RuntimeException(e);} finally {HttpRequest.put("http://localhost:8080/scheduleJobLog").body(JSONUtil.toJsonStr(jobLog)).execute();}}).start();return ResultBean.SUCCESS("hello world!");}
}
在实际的测试过程中,RestRequestJob的实现方法中,由于入库操作在finally中,当业务接口比较简单时,会出现数据库的并发问题,日志记录未入库,导致scheduleJobLogService.getById()方法空指针,因此要先入库再发送http请求业务接口
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {ScheduleJobLog scheduleJobLog = new ScheduleJobLog();ScheduleJob scheduleJob = new ScheduleJob();long snowflakeNextId = IdUtil.getSnowflakeNextId();ScheduleJobLogService logService = SpringUtil.getBean(ScheduleJobLogService.class);try {BeanUtils.copyProperties(context.getMergedJobDataMap().get("TASK_PROPERTIES"), scheduleJob);scheduleJobLog.setExecuteRes(0); // 正在执行scheduleJobLog.setStartTime(new Date());scheduleJobLog.setId(snowflakeNextId);scheduleJobLog.setSchdJobId(scheduleJob.getId());logService.save(scheduleJobLog); // 避免出现并发问题,应该先入库log.info("calling...{}", scheduleJob.getUrl()+"?logId="+snowflakeNextId);String reqUrl = scheduleJob.getUrl()+"?logId="+snowflakeNextId;HttpResponse httpRes = HttpRequest.get(reqUrl).timeout(10000).execute();log.info("logId" + snowflakeNextId + "; url: " + reqUrl);if (httpRes.getStatus() == HttpStatus.HTTP_NOT_FOUND) {throw new Exception(String.valueOf(HttpStatus.HTTP_NOT_FOUND));}} catch (Exception e) {scheduleJobLog.setExecuteRes(-1); // 执行失败scheduleJobLog.setErrorInfo(e.getMessage());logService.saveOrUpdate(scheduleJobLog);throw new RuntimeException(e);}}
总结
在本文中重点是讲解实现思路,代码并不算怎么优雅,格式优化的地方还有很多,参数校验也不够严谨,可以根据自己的需求改造。
代码拉取
开源项目,最好就是各路大神能一起维护这个项目。
仓库地址:https://gitcode.net/interestANd/quartz-job
相关文章:
基于quartz实现定时任务管理系统
基于quartz实现定时任务管理系统 背景 说起定时任务框架,首先想到的是Quartz。这是定时任务的老牌框架了,它的优缺点都很明显。借助PowerJob 的readme文档的内容简单带过一下这部分。 除了上面提到,还有elastic-job-lite、quartzui也是相当…...
vue-element-admin:基于element-ui 的一套后台管理系统集成方案
文章目录一、vue-element-admin1、vue-element-admin1.1简介1.2安装2、vue-admin-template2.1简介2.2安装一、vue-element-admin 1、vue-element-admin 1.1简介 vue-element-admin是基于element-ui 的一套后台管理系统集成方案。 GitHub地址:https://github.com…...
KVM-7、KVM 虚拟机创建的几种方式
通过对 qemu-kvm、libvirt 的学习,总结三种创建虚拟机的方式: (1)通过 qemu-kvm 创建 (2)通过 virt-install 创建 (3)通过 virt-manager 创建 在使用这三种创建虚拟机前提是 宿主机必须支持 cpu 的硬件虚拟化技术(Intel 是 vmx,AMD 是svm),通过下面方式进行查看…...
Hadoop三大框架之HDFS
一、概述HDFS产生的背景及定义HDFS产生背景随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,需要一种系统来管理多台机器上的文件,这就是分布式文件…...
好好的系统,为什么要分库分表?
不急于上手实战 ShardingSphere 框架,先来复习下分库分表的基础概念,技术名词大多晦涩难懂,不要死记硬背理解最重要,当你捅破那层窗户纸,发现其实它也就那么回事。 什么是分库分表 分库分表是在海量数据下࿰…...
多种调度模式下的光储电站经济性最优储能容量配置分析(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
二分法(适用于任何题型!!!)
今天看二分法看了一天,看吐了,现在讲讲怎么做类题。 只讲两种做法(实则是可合并为一种),任何题型都可以转化为这种做法!!!是任何! 首先,设置 int left,righ…...
js常见的七种继承及实现
在 JavaScript 中,常见的继承方式有以下七种:大厂面试题分享 面试题库前后端面试题库 (面试必备) 推荐:★★★★★地址:前端面试题库1. 原型链继承原型链继承是 JavaScript 中一种基于原型的继承方式&#…...
案例分析之——理由Mybatis动态SQL实现复用
无复用思想的做法: 在没有复用思想的时候,就只顾着实现功能。比如开发过程中涉及到两个表的更新功能,每需要更新一处,就写一个接口,结果出现了写了11个接口的情况。 这样虽然功能实现了,可是可能自…...
MCM 箱模型建模方法及大气 O3 来源解析实用干货
OBM 箱模型可用于模拟光化学污染的发生、演变过程,研究臭氧的生成机制和进行敏感性分析,探讨前体物的排放对光化学污染的影响。箱模型通常由化学机理、物理过程、初始条件、输入和输出模块构成,化学机理是其核心部分。MCM (Master Chemical M…...
【独家】华为OD机试 - 最长连续交替方波信号(C 语言解题)
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...
代码随想录算法训练营第二十一天打卡 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先
打卡第21天,继续二叉树,前几天终于补完了,感觉难度上来了。 今日任务 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数 二叉树的最近公共祖先 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root ,返回 树中任意两不…...
免费下载丨一看即会,Serverless 技术进阶必读百宝书
过去一年,全球正在加速推进云计算的 Serverless 化进程。Serverless 架构已经逐渐从“被接受”走向了“被学习”和“被应用”。云的产品体系正在 Serverless 化,从计算、存储、数据库到中间件,越来越多的云产品采用了 Serverless 模式。服务器…...
SQL语句的加锁方式 - Mysql 锁机制
SQL语句的加锁方式 - Mysql锁机制 SELECT ... FROM SELECT ... FOR UPDATE / SELECT ... FOR SHARED MODE SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE UPDATE ... WHERE ... DELETE FROM ... WHERE ... INSERT INSERT ... ON DUPLICATE KEY UPDATE REPLACE Mysql锁机…...
C#开发的OpenRA的游戏主界面怎么样创建4
继续游戏主界面创建的主题, 前面已经说到怎么样找到mainmenu.yaml来显示主界面,也说了怎么样找到各个子控件类。 现在就来仔细分析一下,主界面每一部分的功能。 比如下面这个区域的界面是怎么样创建: 要创建这一小部分的界面显示,也是需要做很多的工作。 因为在这里所有UI…...
覆盖5大主流开发平台的报表控件,它值得你一看
为什么大家现在都在使用第三方报表工具呢? 第三方报表工具是数据库存储,数据库程序通常可以存放的数据量是相当大的,可以处理非常复杂的数据结构关系,报表数据交互速度也非常快。不仅能够提高开发效率,还能实现灵活美…...
【冲刺蓝桥杯的最后30天】day4
大家好😃,我是想要慢慢变得优秀的向阳🌞同学👨💻,断更了整整一年,又开始恢复CSDN更新,从今天开始更新备战蓝桥30天系列,一共30天,如果对你有帮助或者正在备…...
spring boot actuator 动态修改日志级别
1 日志级别 Spring Boot Actuator包括在运行时查看和配置应用程序日志级别的功能。您可以查看整个列表,也可以查看单个记录器的配置,该配置由显式配置的日志级别和日志框架给出的有效日志级别组成。这些级别可以是: TRACEDEBUGINFOWARNERRORFATALOFFnu…...
兴达易控Modbus转Profinet网关连接1200Profinet转modbus接三菱A800变频器案例
下面介绍A800 变频器通过兴达易控modbus转profinet网关,使1200plc无需编程实现Profinet转modbus协议转换,把modbus变频器轻松组网 网络拓扑如下图 打开博图组态加载GSD文件,modbus转profinet网关从站接口接入到1200PLC上 配置modbus转profine…...
「SAP ABAP」OPEN SQL(四)【FROM语句】
💂作者简介: THUNDER王,一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读,同时任汉硕云(广东)科技有限公司ABAP开发顾问。在学习工作中,我通常使用偏后…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
