利用Spring中的SchedulingConfigurer实现数据库配置化定时任务

news2024/10/7 16:24:10

目录

1.利用@Scheduled来实现传统的定时任务

2.两者的区别

3.Spring中的SchedulingConfigurer来拓展定时任务的灵活性

1)UrTaskConfig

2)TaskMain

3)BaseTask

4)效果

(1)插入配置定时任务的sql语句

(2)写一个实现类去继承BaseTask

代码示例:

pom.xml

enums枚举类

EnumTask

EnumTaskRule

数据库Dao操作

UrConfigAllDao

UrTaskTestDao

BaseTask

TaskMain

UrTaskConfig

TaskTextJob

数据库

ur_config_all

ur_task_test


1.利用@Scheduled来实现传统的定时任务

实现定时任务的方式有多种,其中最广为人知的一种是利用@Scheduled,来在代码里面实现代码的定时任务。但是这样简单粗暴的方式可以实现定时任务的功能,但是在我们之后的开发过程中会频繁的使用到定时任务,如果拓展定时任务实现方式的灵活性与拓展性就成了问题。

直接使用@Scheduled的方式来实现定时任务有是有效,但是每增加一个定时任务,或者要修改定时任务的规则,比如修改cron表达式为固定频率或者固定次数,每次修改都要重启一下服务,为我们的开发带来了许多不方便的地方。因为想了一下,可以利用SPring中的SchedulingConfigurer来实现定时任务的拓展性

@Component
@Slf4j
public class MonthEndDataSync {
    @Autowired
    private CarBrandService carBrandService;
    @Autowired
    private CarModelsService carModelsService;
    @Autowired
    private CarSeriesService carSeriesService;
    @Scheduled(cron = "0 0 3 L *  *")
    public void MonthEndDataSync() {
        carBrandService.syncCarBrand4Network();
        carModelsService.syncCarModels4Network();
        carSeriesService.syncCarSeries4Network();
        log.debug("[MonthEndDataSync start] 月底同步数据完成");

    }

2.两者的区别

1.@Scheduled不支持动态修改定时周期,只能停止服务器,修改cron表达式,再启动服务器;SchedulingConfigurer可以动态修改

2.@Scheduled只能是单线程,而SchedulingConfigurer默认是单线程,可以通过添加线程池,实现多线程下定时任务的运行

3.Spring中的SchedulingConfigurer来拓展定时任务的灵活性

总体思路:

1)UrTaskConfig

UrTaskConfig这一类用来获取定时任务的一些配置,定时任务的规则,定时任务的状态,以及定时任务的类型(cron表达式、固定频率、固定间隔)

2)TaskMain

实现SchedulingConfigurer配置Spring的定时任务调度器,在TaskMain类中实现configureTasks方法,根据不同的定时任务规则来实现不同的定时任务。

3)BaseTask

是一个抽象类实现Runnable接口,实现了Runnable接口。这意味着BaseTask的实例可以被当作线程来运行,即可以被提交给线程池或者由Thread对象直接启动。

在BaseTask中定义了三个抽象方法before(),excute(),after(),方法需要由具体的任务子类来实现,可以创建多个不同的任务类,它们都继承自BaseTask,并实现自己的before(),excute(),after()方法。

4)效果

设置一个每5秒执行的定时任务

最终的效果,在我们实际开发的过程中想要实现一个定时任务就只需要做两步

(1)插入配置定时任务的sql语句

例如

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (1, 'TASK_WORK', 'TaskTextJob', 'CORN_RULE', '*/5 * * * * *');

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (2, 'TASK_WORK', 'TaskTextJob', 'CORN_STATUS', '1');

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (3, 'TASK_WORK', 'TaskTextJob', 'CORN_TYPE', '1');

(2)写一个实现类去继承BaseTask

这样就会开启一个定时任务例如:TaskTextJob

代码示例:

pom.xml
<?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>

<groupId>org.example</groupId>
<artifactId>untitled</artifactId>
    <version>1.0-SNAPSHOT</version><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>16</source><target>16</target></configuration></plugin></plugins></build>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot-starter.version>3.1.1</spring-boot-starter.version>
        <mybatis-plus-boot-starter.version>3.5.3.1</mybatis-plus-boot-starter.version>
        <fastjson.version>2.0.32</fastjson.version>
        <mysql-connector-j.version>8.0.33</mysql-connector-j.version>
        <disruptor.version>3.4.2</disruptor.version>
        <spring-boot-starter-web.version>3.1.1</spring-boot-starter-web.version>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.1</version>
        <relativePath/> <!-- lookup parent from repository -->
            </parent>
    <dependencies>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus-boot-starter.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql-connector-j.version}</version>
            </dependency>

            <dependency>
                <groupId>com.lmax</groupId>
                <artifactId>disruptor</artifactId>
                <version>${disruptor.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.28</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <version>3.1.1</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring-boot-starter-web.version}</version>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.18</version>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-log4j2</artifactId>
                <version>3.1.1</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
                <version>3.1.1</version>
            </dependency>

        </dependencies>

</project>
enums枚举类
EnumTask
public enum EnumTask {
    corn_task("TASK_WORK", "类名", "CORN_RULE", "定时任务"),

    corn_task_status("TASK_WORK", "类名", "CORN_STATUS", "定时任务开关"),

    corn_task_type("TASK_WORK", "类名", "CORN_TYPE", "定时任务类型")
    ;

    private final String tableName;
    private final String columnName;
    private final String sId;
    private final String valuee;


    EnumTask(String tableName, String columnName, String sId, String valuee) {
        this.columnName = columnName;
        this.sId = sId;
        this.valuee = valuee;
        this.tableName = tableName;
    }

    public String getTableName() {
        return tableName;
    }

    public String getColumnName() {
        return columnName;
    }

    public String getsId() {
        return sId;
    }

    public String getValuee() {
        return valuee;
    }
}
EnumTaskRule
public enum EnumTaskRule {
    ONE("1","corn表达式"),
    TWO("2","固定频率"),
    THREE("3","固定间隔"),

    STATUS1("1","开启"),
    STATUS0("0","禁用")
    ;
    private final String code;
    private final String msg;


    EnumTaskRule(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}
数据库Dao操作
UrConfigAllDao
public interface UrConfigAllDao extends BaseMapper<UrConfigAll> {
    @Select("""
            select TABLE_NAME, COLUMN_NAME, S_ID, VALUEE,ID
            from ur_config_all
            where TABLE_NAME = #{tableName}
            and COLUMN_NAME = #{columnName}
            and S_ID = #{sId}
            """)
    UrConfigAll queryUrConfigAll(String tableName, String columnName, String sId);
}
UrTaskTestDao
public interface UrTaskTestDao extends BaseMapper<UrTaskTest> {

    @Select("""
            select ID, TASK_NAME, PROCESS_ID, CREATE_TIME, UPDATE_TIME,VERSION,STATUS
            ,CLASS_NAME,METHON,CRON,CRON_TYPE,NEXT_EXEC_TIME
            from ur_task_test
            where TASK_NAME = #{taskName}
            """)
    UrTaskTest selectByTaskName(String taskName);

    @Update("""
            update ur_task_test set
            PROCESS_ID = #{processId},
            UPDATE_TIME = #{updateTime},
            VERSION = VERSION+1
            where  TASK_NAME = #{taskName} and  VERSION=#{version}
            """)
    int updateProcessorIDandVersionByTaskName(UrTaskTest urTaskTest);
    @Delete("""
            DELETE FROM ur_task_test;
            """)
    int deleteAll();
    @Insert("""
            insert into ur_task_test (TASK_NAME, PROCESS_ID,
            CREATE_TIME, UPDATE_TIME,VERSION,STATUS,CLASS_NAME,METHON,CRON,CRON_TYPE,NEXT_EXEC_TIME
            )
            values (#{taskName}, #{processId},
            #{createTime},  #{updateTime},
            #{version},  #{status},  #{className}
            ,  #{methon} ,  #{cron}, #{cronType},#{nextExecTime}
            )
            """)
    int insert2(UrTaskTest urTaskTest);
}
BaseTask
@Component
public abstract class BaseTask implements Runnable{
    private final static Logger log = LoggerFactory.getLogger(BaseTask.class);

    @Autowired
    private UrConfigAllService urConfigAllService;
    @Autowired
    private UrTaskTestService taskTestService ;
    private String taskName;

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    //获取数据库中关于定时任务的配置
    UrTaskConfig getUrTaskConfig(){
        UrTaskConfig urTaskConfig=new UrTaskConfig();
        return urTaskConfig.getUrTaskConfig(taskName,urConfigAllService);
    }
    //执行前
    public abstract void before();
    //执行
    public abstract void execute();
    //执行后
    public abstract void after();

    @Override
    public  void run(){
        //生成任务进程uuid
        String uuid = UUID.randomUUID().toString();
        //根据taskName在ur_task_test表中查询执行进程
        UrTaskTest urTaskTest = taskTestService.selectByTaskName(taskName);
        //获取任务进程的status
        if (urTaskTest==null){
            System.out.println("任务 :" + taskName + " 进程不存在");
            return;
        }
        //判断status,如果是禁用就直接return
        if (StringUtils.equals(String.valueOf(urTaskTest.getStatus()),EnumTaskRule.STATUS0.getCode()))return;
       
        //uuid设置为进程id
        urTaskTest.setProcessId(uuid);
        //版本也是
        urTaskTest.setVersion(urTaskTest.getVersion());
        //更新ur_task_test中的字段
        System.out.println("更新ur_task_test中的字段"+urTaskTest);
        int i=taskTestService.updateProcessIDandVersionByTaskName(urTaskTest);
        //更新返回为0,则该任务已经在其他节点执行
        if (i==0){
            System.out.println("任务 :" + taskName + " 已经在其他节点执行");
            return;
        }
        //更新成功则任务进程开始
        System.out.println("任务 :" + uuid + " 执行开始");

        try {

            //前置处理
            before();

            execute();

            //后置处理
            after();

        }catch (Exception e){
            throw new RuntimeException(e);
        }finally {
            urTaskTest.setTaskName(taskName);
            urTaskTest.setProcessId(null);
            urTaskTest.setUpdateTime(new Date());
            System.out.println("任务进程查看urTaskTest的值 :" + urTaskTest + " 运行结束");
            taskTestService.updateProcessIDandVersionByTaskName(urTaskTest);
            System.out.println("任务进程 :" + uuid + " 运行结束");
        }







    }


}
TaskMain
@Component
public class TaskMain implements SchedulingConfigurer {

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private UrTaskTestService urTaskTestService;
    private final static Logger log = LoggerFactory.getLogger(TaskMain.class);

    //注册定时任务
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {

        //获取所有的定时任务,它们都是BaseTask类的实例或其子类
        Map<String, BaseTask> map = applicationContext.getBeansOfType(BaseTask.class);
        System.out.println("获取所有的定时任务"+map);

        //开始线程数
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
        //清除所有的进程
        urTaskTestService.deleteAll();
        System.out.println("获取所有的定时任务111"+map);
        //遍历map(存储所有BaseTask bean的Map),针对每个bean执行相应的操作
        for (String key:map.keySet()){
            //为每个定时任务创建一个UrTaskTest对象,并设置其属性,如创建时间、任务名称、版本等
            System.out.println("为每个定时任务创建一个UrTaskTest对象"+key);
            UrTaskTest urTaskTest=new UrTaskTest()
            .setCreateTime(new Date())
            .setTaskName(key)
            .setVersion(0L);

            //从Map中获取当前key对应的BaseTask对象,并设置其任务名称taskNmae
            BaseTask baseTask = map.get(key);
            baseTask.setTaskName(key);
            //从BaseTask对象中获取TaskConfigAll对象,并获取其配置信息
            UrTaskConfig urTaskConfig = baseTask.getUrTaskConfig();
            String cornStatus = urTaskConfig.getCornStatus();
            String taskName = urTaskConfig.getTaskName();
            //如果定时任务的cronStatus为TaskEnum.status0.getCode(),表示任务已停止,则跳过当前循环。
            if (StringUtils.equals(cornStatus, EnumTaskRule.STATUS0.getCode())){
                System.out.println("{} 定时任务配置状态为禁用"+urTaskConfig);
                continue;
            }
            //设置urTaskControl的状态为TaskEnum.status1.getCode()
            urTaskTest.setStatus(EnumTaskRule.STATUS1.getCode());
            //如果cron(定时规则)为空,则记录日志并跳过当前循环
            String corn = urTaskConfig.getCorn();
            if (StringUtils.isEmpty(corn)){
                System.out.println("{} 定时任务没配置定时的规则规则"+taskName);
                continue;
            }
            //设置urTaskControlAll的其他属性,如cronType、cron、methon和className
            urTaskTest.setCronType(urTaskConfig.getCornModern());
            urTaskTest.setCron(corn);
            urTaskTest.setMethon("execute()");
            urTaskTest.setClassName(baseTask.getClass().getName());
            //根据urTaskConfig.getCornModern()的值判断注册何种类型的定时任务
            try {
                //如果urTaskConfig.getCornModern()的值为TaskEnum.ONE.getCode(),则注册一个基于CronTrigger的触发任务
                if (EnumTaskRule.ONE.getCode().equals(urTaskConfig.getCornModern())){

                    taskRegistrar.addTriggerTask(baseTask,sheduledConfig->{
                        Date date = new CronTrigger(corn).nextExecutionTime(sheduledConfig);
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String formattedDate = sdf.format(date);
                        // 打印格式化后的日期
                        System.out.println(taskName + "执行定时任务的时间:" + formattedDate);
                        return date.toInstant();
                    });
                    //如果urTaskConfig.getCornModern()的值为TaskEnum.TWO.getCode(),则注册一个固定频率的定时任务
                }else if (EnumTaskRule.TWO.getCode().equals(urTaskConfig.getCornModern())){
                    int parseInt = Integer.parseInt(corn);
                    taskRegistrar.addFixedRateTask(baseTask,parseInt);
                    System.out.println("已添加固定频率任务,频率为:" + parseInt);
                    //如果urTaskConfig.getCornModern()的值为TaskEnum.THREE.getCode(),则注册一个固定次数的定时任务
                }else if (EnumTaskRule.THREE.getCode().equals(urTaskConfig.getCornModern())){
                    int fixedDelay = Integer.parseInt(corn);
                    taskRegistrar.addFixedDelayTask(
                            baseTask, fixedDelay
                    );
                }else {
                    //最后判断是否有定时任务的规则
                    System.out.println("{} 定时任务未找到动态方法"+taskName);
                    continue;
                }
                //插入urTaskTest一条数据到定时任务的进程表中
                urTaskTestService.insert2(urTaskTest);
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }
    }
}
UrTaskConfig
@Data
@Accessors(chain = true)
public class UrTaskConfig {
    private final static Logger log = LoggerFactory.getLogger(UrTaskConfig.class);
    //定时任务类名
    private String taskName;
    //定时任务规则
    private String corn;
    //定时任务状态
    private String cornStatus;
    //定时任务的类型
    private String cornModern;

    public UrTaskConfig getUrTaskConfig(String taskName, UrConfigAllService urConfigAllService){
        //获取定时任务规则
        UrConfigAll urConfigAll = urConfigAllService.getUrConfigAll(EnumTask.corn_task.getTableName(), taskName, EnumTask.corn_task.getsId());
        String rule = Optional.ofNullable(urConfigAll).map(UrConfigAll::getValuee).orElse(null);

        //获取定时任务状态
        UrConfigAll urConfigAll1 = urConfigAllService.getUrConfigAll(EnumTask.corn_task_status.getTableName(), taskName, EnumTask.corn_task_type.getsId());
        String ruleState = Optional.ofNullable(urConfigAll1).map(UrConfigAll::getValuee).orElse(null);

        //定时任务任务类型
        UrConfigAll urConfigAll2 = urConfigAllService.getUrConfigAll( EnumTask.corn_task_type.getTableName(), taskName,EnumTask.corn_task_type.getsId());
        String ruleType = Optional.ofNullable(urConfigAll2).map(UrConfigAll::getValuee).orElse(EnumTaskRule.ONE.getCode());
        UrTaskConfig urTaskConfig=new UrTaskConfig()
        .setTaskName(taskName)
        .setCorn(rule)
        .setCornStatus(ruleState)
        .setCornModern(ruleType);
        System.out.println("定时任务配置参数"+JSON.toJSONString(urTaskConfig) );
        return urTaskConfig;
    }
}
TaskTextJob
@Component
public class TaskTextJob extends BaseTask {


    @Override
    public void before() {

    }

    @Override
    public void execute() {
        // 获取当前时间
        LocalDateTime currentTime = LocalDateTime.now();

        // 定义格式化模板,精确到秒
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 格式化当前时间
        String formattedTime = currentTime.format(formatter);

        // 输出格式化后的时间
        System.out.println("执行定时任务测试: " + formattedTime);
    }

    @Override
    public void after() {

    }
}

数据库
ur_config_all
CREATE TABLE `ur_config_all` (

`ID` int NOT NULL AUTO_INCREMENT COMMENT '主键',

`TABLE_NAME` varchar(255) NOT NULL,

`COLUMN_NAME` varchar(255) NOT NULL,

`S_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,

`VALUEE` varchar(255) NOT NULL,

PRIMARY KEY (`ID`)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ur_task_test
CREATE TABLE `ur_task_test` (

`ID` int NOT NULL AUTO_INCREMENT COMMENT '任务ID',

`TASK_NAME` varchar(255) NOT NULL COMMENT '任务名',

`PROCESS_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '进程ID',

`CREATE_TIME` datetime NOT NULL COMMENT '创建时间',

`UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',

`VERSION` bigint NOT NULL COMMENT '版本',

`STATUS` varchar(50) NOT NULL COMMENT '状态',

`CLASS_NAME` varchar(255) NOT NULL COMMENT '类路径',

`METHON` varchar(255) NOT NULL COMMENT '方法',

`CRON` varchar(255) DEFAULT NULL COMMENT '定时规则 有2种值,表达式和毫秒',

`CRON_TYPE` varchar(50) DEFAULT NULL COMMENT '定时任务类型 1:cron表达式,2:固定频率,3:固定间隔',

`NEXT_EXEC_TIME` datetime DEFAULT NULL COMMENT '任务名',

PRIMARY KEY (`ID`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

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

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

相关文章

参数传递 的案例

文章目录 12 1 输出一个int类型的数组&#xff0c;要求为&#xff1a; [11,22,33,44,55] package com.zhang.parameter; //有关方法的案例 public class MethodTest3 {public static void main(String[] args) {//输出一个int类型的数组&#xff0c;要求为&#xff1a; [11,…

UE4网络图片加载库(带内存缓存和磁盘缓存)

UE4网络图片加载库,带内存缓存和磁盘缓存,支持自定义缓存大小,支持蓝图和C++代码调用 1、调用示例 2、对外暴露函数 3、源代码-网络模块 KeImageNet.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreM…

在控制台实现贪吃蛇

在控制台实现贪吃蛇 前备知识Win32APICOORD这个结构体的声明如下&#xff1a;GetStdHandle 函数GetConsoleCursorInfo 函数SetConsoleCursorInfo 函数 SetConsoleCursorPosition 函数getAsyncKeyState 函数 控制台窗口的大小以及字符打印介绍控制台中的坐标宽字符及本地化介绍s…

的记忆:pandas(实在会忘记,就看作是一个 Excel 表格,或者是 SQL 表,或者是字典的字典。)

pandas 是一个开源的 Python 数据分析库&#xff0c;它提供了快速、灵活和富有表现力的数据结构&#xff0c;旨在使“关系”或“标记”数据的“快速分析、清洗和转换”变得既简单又直观。pandas 非常适合于数据清洗和转换、数据分析和建模等任务。以下是 pandas 的基本概念和主…

使用 Flutter 打造引人入胜的休闲游戏体验

作者 / Zoey Fan 去年&#xff0c;Flutter 休闲游戏工具包进行了一次重大更新。近期在旧金山举办的游戏开发者大会 (GDC) 上&#xff0c;Flutter 首次亮相。GDC 是游戏行业的顶级专业盛会&#xff0c;致力于帮助游戏开发者不断提升开发技能。欢迎您继续阅读&#xff0c;了解开发…

Redis(四) 主从、哨兵、集群环境搭建

结合前三期 Redis(一) Redis简介(Redis(一) Redis简介-CSDN博客) Redis(二) 可编程性(Redis(二) 可编程性-CSDN博客) Redis(三) 事务与发布订阅(Redis(三) 事务与发布订阅-CSDN博客) 目录 1.0 Redis主从 1.1 Redis 主从结构的基本原理和工作方式 1.2 Redis 主从结构的好处 …

经典案例|使用Supabase解决可视化大屏项目的常见问题

敏博科技专业致力于应急管理行业&#xff0c;提供以物联网技术和感知预警算法模型为核心的先进产品和解决方案。应急管理行业的业务非常繁多和复杂&#xff0c;很多时候都需要在短时间内交付出稳定高效的业务系统。如下两张图某市的安全生产监测预警系统 MemFire Cloud应用开…

图像处理之模板匹配(C++)

图像处理之模板匹配&#xff08;C&#xff09; 文章目录 图像处理之模板匹配&#xff08;C&#xff09;前言一、基于灰度的模板匹配1.原理2.代码实现3.结果展示 总结 前言 模板匹配的算法包括基于灰度的匹配、基于特征的匹配、基于组件的匹配、基于相关性的匹配以及局部变形匹…

Maven的下载和环境变量的配置

1下载 maven官网https://maven.apache.org/index.html点击Download 这个是Windows的下载版本&#xff0c;一般是最新的版本&#xff0c; 老的版本下载 以下是对应的老版本下载链接 下载好后是一个压缩包的形式 点击他进行解压到想放的文件夹中&#xff0c;&#xff08;一般不…

设计模式-00 设计模式简介之几大原则

设计模式-00 设计模式简介之几大原则 本专栏主要分析自己学习设计模式相关的浅解&#xff0c;并运用modern cpp 来是实现&#xff0c;描述相关设计模式。 通过编写代码&#xff0c;深入理解设计模式精髓&#xff0c;并且很好的帮助自己掌握设计模式&#xff0c;顺便巩固自己的c…

【神经网络基础辨析】什么是神经网络的主干(backbone)、颈部(neck)和头部(head)网络

在神经网络中&#xff0c;通常将网络分为三个部分&#xff1a;骨干网络&#xff08;Backbone&#xff09;、颈部网络&#xff08;Neck&#xff09;、和头部网络&#xff08;Head&#xff09;。 骨干网络&#xff08;Backbone&#xff09; 骨干网络通常是神经网络的主要部分&a…

探索设计模式的魅力:主从模式与AI大模型的结合-开启机器学习新纪元

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索主从模式与AI大模型之旅✨ &#x1f31f;Hey, tech enthusiasts! 你是否还在追…

【嵌入式AI部署神经网络】STM32CubeIDE上部署神经网络之指纹识别(Pytorch)——篇一|环境搭建与模型初步部署篇

前言&#xff1a;本篇主要讲解搭建所需环境&#xff0c;以及基于pytorch框架在stm32cubeide上部署神经网络&#xff0c;部署神经网络到STM32单片机&#xff0c;本篇实现初步部署模型&#xff0c;没有加入训练集与验证集&#xff0c;将在第二篇加入。篇二详细讲解STM32CubeIDE上…

PHP之内置web服务器

1. 前言 PHP从5.4开始&#xff0c;就提供了一个内置的web服务器。 这个主要是用来做本地的开发测试用的&#xff0c;不能用于线上环境。 将PHP的安装路径配置到电脑的系统环境变量Path中&#xff0c;下图是win7&#xff0c;win10中会看的更清楚 2. 进入项目目录&#xff0c;执…

OpenHarmony南向开发案例:【 智能家居中控】

应用场景简介 智能家居。 今天打造的这一款全新智能家庭控制系统&#xff0c;凸显应用在智能控制和用户体验的特点&#xff0c;开创国内智能家居系统体验新局面。新的系统主要应用在鸿蒙生态。 工程版本 系统版本/API版本&#xff1a;OpenHarmony SDK API 8IDE版本&#xf…

unity cinemachine相机 (案例 跟随角色移动)

安装相机包 打开包管理工具 在 unity registry 搜索cinemachine 会在maincamera中生成一个组件cinemachineBrain 只能通过虚拟相机操控 主相机 虚拟相机的参数 案例 1.固定相机效果 位置 在固定的地方 默认的模式 2.相机跟随人物效果 焦距设置 20 跟随设置 把playere…

【LeetCode热题100】【多维动态规划】不同路径

题目链接&#xff1a;62. 不同路径 - 力扣&#xff08;LeetCode&#xff09; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记…

Django模型继承之Meta继承

在Django模型继承中&#xff0c;当一个抽象基类被设计完成后&#xff0c;它会将该基类中定义的Meta内部类以属性的形式提供给子类。另外&#xff0c;如果子类未定义自己的Meta类&#xff0c;那么它就会默认继承抽象基类的Meta类。 关于Meta类的继承&#xff0c;大致总结如下&a…

Ubuntu20.04安装redis5.0.7

redis下载命令&#xff1a; wget https://download.redis.io/releases/redis-5.0.7.tar.gz 解压到 opt目录下 tar -zxvf redis-5.0.7.tar.gz -C /opt apt install -y gcc # 安装gccapt install make # 安装make 后面执行make一直报错 make报错后清除&#xff1a; make …

机器学习(XgBoost)预测顶和底

之前的文章中&#xff0c;我们对中证1000指数进行了顶和底的标注。这一篇我们将利用这份标注数据&#xff0c;实现机器学习预测顶和底&#xff0c;并探讨一些机器学习的原理。 我们选取的特征非常简单–上影线和WR&#xff08;William’s R&#xff09;的一个变种。选取这两个…