Quartz任务调度框架实现任务动态执行

说明:之前使用Quartz,都是写好Job,指定一个时间点,到点执行。最近有个需求,需要根据前端用户设置的时间点去执行,也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行。

基本实现

(0)思路

基本思路是,根据用户传入的时间点,转为cron表达式,定时去刷新容器内的JOB,更新任务的cron表达式。

(1)搭建项目

创建一个项目,pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><groupId>com.hezy</groupId><artifactId>auto_quartz</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><version>42.7.3</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.6</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
</project>

我项目的是postgresql,application.yml如下

server:port: 8988spring:datasource:url: jdbc:postgresql://localhost:5432/demousername: postgrespassword: 123456driver-class-name: org.postgresql.Driver

创建一张任务调度表,包含了任务名(对应Java类中JOB的Bean名称),任务的cron表达式;

-- 创建任务调度表
create table public.t_task
(id         varchar(32)       not nullconstraint pc_task_idprimary key,task_name  varchar(50)       not null,cron       varchar(50)       not null,is_deleted integer default 0 not null
);comment on table public.t_task is '任务调度表';
comment on column public.t_task.task_name is '任务名';
comment on column public.t_task.cron is 'cron表达式';
comment on column public.t_task.is_deleted is '是否删除,1是,0否,默认0';

(2)引入Quartz

创建两个关于Quartz的配置类

(MyJobFactory)

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;/*** 任务工厂*/
@Component
public class MyJobFactory extends AdaptableJobFactory {@Autowiredprivate AutowireCapableBeanFactory capableBeanFactory;/*** 创建JOB实例*/@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {Object jobInstance = super.createJobInstance(bundle);capableBeanFactory.autowireBean(jobInstance);return jobInstance;}
}

(TaskConfig)

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;/*** 任务配置类*/
@Configuration
@DependsOn("myJobFactory")
public class TaskConfig {@Autowiredprivate MyJobFactory myJobFactory;/*** 加载任务工厂*/@Beanpublic SchedulerFactoryBean schedulerFactoryBean() {SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();schedulerFactoryBean.setJobFactory(myJobFactory);return schedulerFactoryBean;}@Beanpublic Scheduler scheduler() {return schedulerFactoryBean().getScheduler();}
}

(3) 数据库操作实现

创建一个TaskDTO

import lombok.Data;@Data
public class TaskDTO {private String id;/*** 任务名*/private String taskName;/*** CRON表达式*/private String cron;
}

写一个对应的Mapper,里面创建一个查询方法

import com.hezy.pojo.TaskDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface TaskMapper {@Select("select id, task_name taskName, cron from t_task where is_deleted = 0")List<TaskDTO> getAllTask();
}

插入两条数据,如下:

insert into public.t_task (id, task_name, cron, is_deleted)
values ('1', 'SimpleTask1', '0/5 * * * * ?', 0);insert into public.t_task (id, task_name, cron, is_deleted)
values ('2', 'SimpleTask2', '0/30 * * * * ?', 0);

测试,没得问题

import com.hezy.mapper.TaskMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class TaskMapperTest {@Autowiredprivate TaskMapper taskMapper;@Testpublic void testInsert() {System.out.println(taskMapper.getAllTask());}
}

在这里插入图片描述

(4)创建任务容器

创建一个任务容器,每10秒去刷新容器内的所有Job的cron表达式

import com.hezy.constant.TaskConst;
import com.hezy.mapper.TaskMapper;
import com.hezy.pojo.TaskDTO;
import lombok.extern.log4j.Log4j2;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.List;/*** 任务容器*/
@Component
@DependsOn(value = {"quartzManager"})
@Log4j2
public class TaskContext {@Autowiredprivate QuartzManager quartzManager;@Autowiredprivate TaskMapper taskMapper;/*** 更新任务* 刷新频率:10秒*/@Scheduled(fixedRate = 10000)public void update() {try {// 查出所有任务List<TaskDTO> taskDTOS = taskMapper.getAllTask();// 遍历操作for (TaskDTO taskDto : taskDTOS) {this.quartzManager.startJobTask(taskDto.getTaskName(), TaskConst.GroupEnum.SYSTEM.name(), taskDto.getCron());}} catch (SchedulerException e) {log.error("初始化定时任务异常", e);}}
}

需要注意,@Scheduled注解,需要在启动类上加@EnableScheduling注解才能生效

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication
@EnableScheduling
public class Start {public static void main(String[] args) {SpringApplication.run(Start.class, args);}
}

创建一个常量,表示任务的组名

(TaskConst)

/*** 任务常量*/
public class TaskConst {/*** 任务分组*/public enum GroupEnum {SYSTEM;}
}

任务管理器,对比传入的任务名、任务cron,更新或创建对应的任务;

import lombok.extern.log4j.Log4j2;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 定时任务管理器* @author hezhongying* @create 2024/12/11*/
@Configuration
@Log4j2
public class QuartzManager {@Autowiredprivate Scheduler scheduler;@Autowiredprivate List<Job> taskList;private Map<String, Job> taskMap;@PostConstructpublic void init() {this.taskMap = taskList.stream().collect(Collectors.toMap(t -> t.getClass().getSimpleName(), t -> t));}/*** 启动任务* @param jobName 任务名* @param group 组名* @param cron cron表达式* @throws SchedulerException*/public void startJobTask(String jobName, String group, String cron) throws SchedulerException {// 创建JobJobKey jobKey = new JobKey(jobName, group);// 判断任务是否已存在,不存在新增,存在更新if (scheduler.checkExists(jobKey)) {if (modifyJob(jobName, group, cron)) {log.info("任务:{} 修改成功", jobName);}} else {if (createJob(jobName, group, cron)) {log.info("任务:{} 新增成功", jobName);}}}/*** 新增任务* @param jobName 任务名称* @param group 分组* @param cron CRON表达式* @throws SchedulerException*/private boolean createJob(String jobName, String group, String cron) throws SchedulerException {// 任务新增if (taskMap.containsKey(jobName)) {// 执行任务Class<? extends Job> taskClazz = taskMap.get(jobName).getClass();JobDetail jobDetail = JobBuilder.newJob(taskClazz).withIdentity(jobName, group).build();// 执行时间正则CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cron);CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, group).withSchedule(cronBuilder).build();scheduler.scheduleJob(jobDetail, cronTrigger);return true;} else {log.debug("任务没有配置执行配置{}", jobName);}return false;}/*** 修改** @param jobName 任务名* @param group 组名* @param cron cron表达式* @return true,修改成功;false,修改失败* @throws SchedulerException*/public boolean modifyJob(String jobName, String group, String cron) throws SchedulerException {TriggerKey triggerKey = new TriggerKey(jobName, group);Trigger trigger = scheduler.getTrigger(triggerKey);if (trigger == null) {log.info("未存在的触发器[{}-{}]", jobName, group);return false;}String oldCron = ((CronTrigger) trigger).getCronExpression();// 判断cron是否相等,相等就不用改if (!cron.equals(oldCron)) {CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);CronTrigger newTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, group).withSchedule(cronScheduleBuilder).build();Date date = scheduler.rescheduleJob(triggerKey, newTrigger);return date != null;} else {log.info("任务:{} CRON表达式没有变化", jobName);}return false;}/*** 暂停所有任务** @throws SchedulerException*/public void pauseAll() throws SchedulerException {this.scheduler.pauseAll();}/*** 指定任务暂停** @param jobName 任务名* @param group 组名* @throws SchedulerException*/public void pause(String jobName, String group) throws SchedulerException {JobKey jobKey = new JobKey(jobName, group);JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);if (jobDetail == null) {return;}this.scheduler.pauseJob(jobKey);}/*** 恢复所有任务** @throws SchedulerException*/public void resumeAllJob() throws SchedulerException {this.scheduler.resumeAll();}/*** 删除指定任务** @param jobName 任务名* @param group 组名* @throws SchedulerException*/public void delete(String jobName, String group) throws SchedulerException {JobKey jobKey = new JobKey(jobName, group);JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);if (jobDetail == null) {return;}this.scheduler.deleteJob(jobKey);}
}

注意这行代码,可自动注入容器内所有的Job,也就是所有实现了Quartz框架Job接口的类;

    @Autowiredprivate List<Job> taskList;

(5)创建两个Job

创建两个简单的Job

(SimpleTask1)

import lombok.extern.log4j.Log4j2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;
import java.util.Date;/*** 简单任务1*/
@Component
@Log4j2
public class SimpleTask1 implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) {log.info("{} 定时任务执行时间:{}", "SimpleTask1", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}
}

(SimpleTask2)

import lombok.extern.log4j.Log4j2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;
import java.util.Date;/*** 简单任务2*/
@Component
@Log4j2
public class SimpleTask2 implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) {log.info("{} 定时任务执行时间:{}", "SimpleTask2", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}
}

(6)启动测试

启动项目,查看控制台

在这里插入图片描述

此时,项目不停,修改数据对应任务的cron表达式,

在这里插入图片描述

查看控制台

在这里插入图片描述

到这,动态调度任务就实现了。

更进一步

回到我最开始提到的需求,再来考虑一些细节。

  • 前端传递的时间如何转为cron表达式;

  • 如何实现不同任务对同一个Job的动态执行;


第一个问题,前端传递的任务数据,可能是这样的,任务“每天、每周、每月”,然后设置一个时间点,要求任务按照这个周期来执行,如果将这些信息转为对应的cron表达式,需要有一段代码来实现这些映射关系。

如下:

    /*** 生成CRON表达式* @param type 类型:每天、每周、每月* @param hour 时* @param minute 分* @param dayOfWeek 星期* @param dayOfMonth 日期* @return CRON表达式或null*/public static String generateCronExpression(int type, int hour, int minute, int dayOfWeek, int dayOfMonth) {switch (type) {case 1:return String.format("0 %d %d * * ?", minute, hour);case 2:return String.format("0 %d %d ? * %s", minute, hour, TaskConst.DayOfWeekEnum.getByCode(dayOfWeek).getName());case 3:return String.format("0 %d %d %d * ?", minute, hour, dayOfMonth);default:return null;}}

(星期映射)

cron表达式末尾不能填对应星期的数字,而需要填星期几的英文简称

    /*** 星期枚举*/public enum DayOfWeekEnum {SUNDAY(1, "SUN"),MONDAY(2, "MON"),TUESDAY(3, "TUE"),WEDNESDAY(4, "WED"),THURSDAY(5, "THU"),FRIDAY(6, "FRI"),SATURDAY(7, "SAT");private final Integer code;private final String name;DayOfWeekEnum(Integer code, String name) {this.code = code;this.name = name;}public Integer getCode() {return code;}public String getName() {return name;}/*** 根据code查询枚举*/public static DayOfWeekEnum getByCode(Integer code) {for (DayOfWeekEnum value : DayOfWeekEnum.values()) {if (value.getCode().equals(code)) {return value;}}return null;}/*** 根据name查询枚举*/public static DayOfWeekEnum getByName(String name) {for (DayOfWeekEnum value : DayOfWeekEnum.values()) {if (value.getName().equals(name)) {return value;}}return null;}}

但这种方式局限于我上面说的,只能“每天、每周、每月”的某时、某分执行,而不能更精准到秒、或者每月的最后一周,这样更灵活的设置。(思考)或许可以考虑直接将cron表达式的设置,开放给前端,由用户直接来设置,后端只需校验cron的合法性即可。


第二个问题,有这样的情况:我的任务调度表中的多条记录,执行的是同一个Job,我的Job中会根据记录的唯一ID去查找这个记录对应的数据来处理。这种情况要怎样,还能实现动态调度。

如下,两条记录,对应同一个Job,但是cron不同,执行周期不同;

在这里插入图片描述

首先,在定时刷新这里,就不能只传入taskName,再传入一个唯一标识,我这里就传入ID;

    /*** 更新任务* 刷新频率:10秒*/@Scheduled(fixedRate = 10000)public void update() {try {// 查出所有任务List<TaskDTO> taskDTOS = taskMapper.taskInfo();// 遍历操作for (TaskDTO taskDto : taskDTOS) {// 带上唯一标识this.quartzManager.startJobTask(taskDto.getTaskName(), taskDto.getId(), TaskConst.GroupEnum.SYSTEM.name(), taskDto.getCron());}} catch (SchedulerException e) {log.error("初始化定时任务异常", e);}}

任务管理器这里,判断是否有当前任务,就用任务名_唯一标识判断,绑定Job的时候,就用Job名,即数据库记录的taskName,如下:

    /*** 启动任务* @param name Job名,即Job类名* @param id 唯一标识* @param group 组名* @param cron cron表达式* @throws SchedulerException*/public void startJobTask(String name, String id, String group, String cron) throws SchedulerException {// 任务名,用Job名和唯一标识拼接String jobName = String.format("%s_%s", name, id);// 创建JobJobKey jobKey = new JobKey(jobName, group);// 判断任务是否已存在,不存在新增,存在更新if (scheduler.checkExists(jobKey)) {if (modifyJob(jobName, id, group, cron)) {log.info("任务:{} 修改成功", jobName);}} else {if (createJob(name, jobName, id, group, cron)) {log.info("任务:{} 新增成功", jobName);}}}

新增Job

    /*** 新增任务* @param name JOB名* @param jobName 任务名称* @param id 唯一标识* @param group 分组* @param cron CRON表达式* @throws SchedulerException*/private boolean createJob(String name, String jobName, String id, String group, String cron) throws SchedulerException {// 任务新增,用Job名判断当前容器中是否存在这个Jobif (taskMap.containsKey(name)) {// 执行任务:获取Job类时,也用Job名Class<? extends Job> taskClazz = taskMap.get(name).getClass();// 创建任务式,用Job和唯一标识拼接作为任务名JobDetail jobDetail = JobBuilder.newJob(taskClazz).withIdentity(jobName, group).build();// 创建 JobDataMap 并放入唯一标识 idJobDataMap jobDataMap = new JobDataMap();jobDataMap.put("id", id);// 执行时间正则CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cron);CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, group).usingJobData(jobDataMap).withSchedule(cronBuilder).build();scheduler.scheduleJob(jobDetail, cronTrigger);return true;} else {log.debug("任务没有配置执行配置{}", jobName);}return false;}

修改Job

    /*** 修改** @param jobName 任务名* @param group 组名* @param cron cron表达式* @return true,修改成功;false,修改失败* @throws SchedulerException*/public boolean modifyJob(String jobName, String id, String group, String cron) throws SchedulerException {TriggerKey triggerKey = new TriggerKey(jobName, group);Trigger trigger = scheduler.getTrigger(triggerKey);if (trigger == null) {log.info("未存在的触发器[{}-{}]", jobName, group);return false;}// 创建 JobDataMap 并放入唯一标识 idJobDataMap jobDataMap = new JobDataMap();jobDataMap.put("id", id);String oldCron = ((CronTrigger) trigger).getCronExpression();// 判断cron是否相等,相等就不用改if (!cron.equals(oldCron)) {CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);CronTrigger newTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, group).usingJobData(jobDataMap).withSchedule(cronScheduleBuilder).build();Date date = scheduler.rescheduleJob(triggerKey, newTrigger);return date != null;} else {log.info("任务:{} CRON表达式没有变化", jobName);}return false;}

Job实现里,打印唯一标识,用于区分。如果你有业务需求,完全可以用这个唯一标识,去查询数据库,找出这个唯一标识所拥有的数据,然后做一系列操作。

/*** 简单任务1*/
@Component
@Log4j2
public class SimpleTask1 implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) {log.info("{} 定时任务执行时间:{}", jobExecutionContext.getMergedJobDataMap().get("id"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}
}

启动程序,可见两条记录各自的执行周期。

在这里插入图片描述

一个Job,各自运行,让博主想起黑塞 《悉达多》中的一句话:可是也有另一些人,一些为数不多的人,像沿着一条固定轨道运行的星星,没有风吹得到它们;它们有自身的规律和轨道。

总结

本文介绍了如何使用Quartz任务调度框架实现任务动态执行,参考下面这篇文章:

  • SpringBoot整合quartz完成定时任务执行配置热修改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891158.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring-kafka快速Demo示例

使用Spring-Kafka快速发送/接受Kafka消息示例代码&#xff0c;项目结构是最基础的SpringBoot结构&#xff0c;提前安装好Kafka&#xff0c;确保Kafka已经正确启动 pom.xml&#xff0c;根据个人情况更换springboot、java版本等 <?xml version"1.0" encoding&qu…

【C++】B2079 求出 e 的值

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目介绍输入格式输出格式输入输出样例说明/提示 &#x1f4af;实现方法一&#xff1a;单层 for 循环计算代码实现运行逻辑解析优点不足 &#x1f4af;实现方法二&#xff…

STM32配合可编程加密芯片SMEC88ST的防抄板加密方案设计

SMEC88ST SDK卡发包下载 目前市场上很多嵌入式产品方案都是可以破解复制的&#xff0c;主要是因为方案主芯片不具备防破解的功能&#xff0c;这就导致开发者投入大量精力、财力开发的新产品一上市就被别人复制&#xff0c;到市场上的只能以价格竞争&#xff0c;最后工厂复制的产…

精准识别花生豆:基于EfficientNetB0的深度学习检测与分类项目

精准检测花生豆&#xff1a;基于EfficientNet的深度学习分类项目 在现代农业生产中&#xff0c;作物的质量检测和分类是确保产品质量的重要环节。针对花生豆的检测与分类需求&#xff0c;我们开发了一套基于深度学习的解决方案&#xff0c;利用EfficientNetB0模型实现高效、准…

MarkItDown的使用(将Word、Excel、PDF等转换为Markdown格式)

MarkItDown的使用&#xff08;将Word、Excel、PDF等转换为Markdown格式&#xff09; 本文目录&#xff1a; 零、时光宝盒&#x1f33b; 一、简介 二、安装 三、使用方法 3.1、使用命令行形式 3.2、用 Python 调用 四、总结 五、参考资料 零、时光宝盒&#x1f33b; &a…

Qanything 2.0源码解析系列6 PDF解析逻辑

Qanything 2.0源码解析系列6: PDF解析逻辑 type: Post status: Published date: 2024/12/04 summary: 深入剖析Qanything是如何拆解PDF的,核心是pdf转markdown category: 技术分享 原文:www.feifeixu.top 😀 前言: 在前面的文章中探究了图片是怎么进行解析的,这篇文章对…

【Agent】Chatbot、Copilot与Agent如何帮助我们的提升效率?

人工智能&#xff08;AI&#xff09;技术的迅猛发展正在深刻改变我们的生活和工作方式。你是否曾想过&#xff0c;未来的工作场景会是什么样子&#xff1f;AI的崛起不仅仅是科技的进步&#xff0c;更是我们生活方式的革命。今天&#xff0c;我们将深入探讨三种主要的AI能力&…

如何使用python读写游戏内存以及使用特征码匹配基址

一.读写内存所需的基本参数 接下来我将使用GTA5游戏举例 1.通过进程名称获取进程pid from psutil import process_iterdef get_process_id_by_name(process_name):for process in process_iter(["pid", "name"]):if process.info["name"] pr…

简述css中z-index的作用?如何用定位使用?

z-index是一个css属性&#xff0c;用于控制元素的堆叠顺序&#xff0c; 如何使用定位用index 1、position&#xff1a;relative&#xff1b; z-index&#xff1b; 相对于自己来定位的&#xff0c;可以根据top&#xff0c;bottom&#xff0c;right&#xff0c;left&#xff…

CCNP_SEC_ASA 第六天作业

实验需求&#xff1a; 为保障内部用户能够访问Internet&#xff0c;请把10.1.1.0/24网络动态转换到外部地址池202.100.1.100-202.100.1.200&#xff0c;如果地址池耗尽后&#xff0c;PAT到Outside接口 提示&#xff1a;需要看到如下输出信息 Inside#telnet 202.100.1.1 Trying …

计算机网络 (13)信道复用技术

前言 计算机网络中的信道复用技术是一种提高网络资源利用率的关键技术。它允许在一条物理信道上同时传输多个用户的信号&#xff0c;从而提高了信道的传输效率和带宽利用率。 一、信道复用技术的定义 信道复用&#xff08;Multiplexing&#xff09;就是在一条传输媒体上同时传输…

敏捷开发Scrum的深入理解和实践

敏捷开发&#xff0c;特别是Scrum方法&#xff0c;已经逐渐成为软件开发领域的主流方法。Scrum不仅适用于软件开发&#xff0c;还适用于其他需要快速响应变化和灵活交付的领域。本文将深入探讨Scrum的核心概念、流程、优势、挑战及其在实践中的应用。 一、Scrum的核心概念 Scru…

计算机视觉目标检测-2

文章目录 摘要abstract1.Fast R-CNN1.1 RoI pooling1.2 End-to -End model1.3 多任务损失-Multi-task loss1.4 R-CNN、SPPNet、Fast R-CNN效果比对 2.Faster R-CNN2.1 RPN原理2.2 效果对比2.3 Faster R-CNN总结 3.总结4.参考文献 摘要 本周学习了Fast R-CNN和Faster R-CNN算法…

JavaScript网页设计案例:动态交互式任务列表

在现代网页开发中&#xff0c;JavaScript被广泛应用于实现动态交互效果。看完这一篇你就可以设计一个动态任务列表&#xff0c;全面展示HTML、CSS和JavaScript在前端开发中的实际应用。通过本案例&#xff0c;你将深入了解事件监听、DOM操作以及用户交互设计的实现过程。 案例需…

【MySQL】索引 面试题

文章目录 适合创建索引的情况创建索引的注意事项MySQL中不适合创建索引的情况索引失效的常见情况 索引定义与作用 索引是帮助MySQL高效获取数据的有序数据结构&#xff0c;通过维护特定查找算法的数据结构&#xff08;如B树&#xff09;&#xff0c;以某种方式引用数据&#xf…

使用Excel制作通达信自定义“序列数据“

序列数据的视频教程演示 Excel制作通达信自定义序列数据 1.序列数据的制作方法&#xff1a;删掉没有用的数据&#xff08;行与列&#xff09;和股代码格式处理&#xff0c;是和外部数据的制作方法是相同&#xff0c;自己上面看历史博文。只需要判断一下&#xff0c;股代码跟随的…

【ELK】ES单节点升级为集群模式--太细了!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言准备工作1. 查看现状【单节点】2. 原节点改集群模式3. 改es配置文件&#xff0c;增加集群相关配置项4. *改docker映射的端口* 启动新节点5. docker-compose起一…

关于区块链的安全和隐私

背景 区块链技术在近年来发展迅速&#xff0c;被认为是安全计算的突破&#xff0c;但其安全和隐私问题在不同应用中的部署仍处于争论焦点。 目的 对区块链的安全和隐私进行全面综述&#xff0c;帮助读者深入了解区块链的相关概念、属性、技术和系统。 结构 首先介绍区块链…

webauthn介绍及应用

1、webauthn介绍 官网&#xff1a;https://webauthn.io/ 1.1、什么是webauthn&#xff1f; webauthn即Web Authentication&#xff0c;是一个符合W3C标准的Web认证规范。它通过公私钥加密技术&#xff0c;实现无密码认证&#xff0c;用户仅需通过pin码、指纹、面部识别、usb …

ElasticSearch如何做性能优化?

大家好&#xff0c;我是锋哥。今天分享关于【ElasticSearch如何做性能优化?】面试题。希望对大家有帮助&#xff1b; ElasticSearch如何做性能优化? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Elasticsearch 是一个开源的分布式搜索引擎&#xff0c;广泛用于…