第二十二章 : Spring Boot 集成定时任务(一)
第二十二章 : Spring Boot 集成定时任务(一)
前言
本章知识点: 介绍使用Spring Boot内置的@Scheduled注解来实现定时任务-单线程和多线程;以及介绍Quartz定时任务调度框架:简单定时调度器(SimpleSchedule
)和Cron表达式调度器(CronSchedule
)来调度触发的定时任务。
Springboot 版本 2.3.2.RELEASE ,RabbitMQ 3.9.11,Erlang 24.2
场景
- 系统维护:操作系统和各种软件通常需要定期进行更新和打补丁。通过定时任务,这些更新可以在预定时间自动进行,无需人工干预。
- 文件同步:在多个设备或服务器之间同步文件时,可以使用定时任务来确保文件的一致性。例如,使用cron在Linux系统上定期同步文件夹。
- 发送通知或警报:许多系统需要定期发送通知或警报,如系统状态报告,警报阈值越过通知等。这些可以通过定时任务来实现。
- 数据抽取和加载:在大数据环境中,通常需要从不同的数据源抽取数据,并将其加载到数据仓库或分析系统中。定时任务可以定期执行这些操作。
- 测试和监控:定期进行系统或应用的健康检查,性能测试等,以便及时发现问题并进行修复。
- 日志清理:定期清理过期的日志文件,以防止磁盘空间被耗尽。
- 网关定期同步:对于需要和远程系统进行同步的网关服务,定时任务可以帮助实现数据的定期更新。
- 定期重新启动服务:某些服务可能需要定期重新启动以保持良好的性能。定时任务可以执行这个操作。
- 定期更新统计信息:在各种系统中,统计信息的及时更新对于决策制定和性能优化都非常重要。定时任务可以帮助实现这个需求。
Spring Boot定时任务的方式
Spring Boot提供了两种实现定时任务的方式:
1) 一种是Spring Boot内置的注解方式,只需在类上增加@Scheduled即可实现;
2)另一种是基于Quartz实现,Quartz是目前完善的定时任务解决方案,适合处理复杂的应用场景。
@Scheduled定时任务
参数说明
value
:指定计划任务的时间间隔。可以是固定时间间隔的字符串表示,例如"0 0/5 * * * ?"表示每5分钟执行一次。也可以是cron表达式,例如"0 0 12 * * ?"表示每天中午12点执行。fixedRate
:固定速率,表示任务以固定的时间间隔执行。如果设置了该参数,那么方法会在每隔一定时间间隔后执行一次。与value
参数类似,参数值可以是固定的时间间隔字符串或cron表达式。fixedDelay
:固定延迟,表示任务在完成一次执行后,等待一定的延迟时间再执行下一次。与value
参数类似,参数值可以是固定的时间间隔字符串或cron表达式。initialDelay
:初始延迟,表示任务在启动后需要等待一定的延迟时间才开始执行。参数值可以是固定的时间间隔字符串或cron表达式。cron
:cron表达式,用于指定任务的执行时间。可以精确到秒级别。例如,"0 0 12 * * ?"表示每天中午12点执行。timezone
:时区,用于指定任务的执行时间。该参数通常与cron表达式一起使用,以确定任务在特定时区中的执行时间。threadName
:线程名称,用于指定执行任务的线程名称。如果未设置该参数,则默认使用"ScheduledThreadExecutor"。inheritable
:是否可继承,表示是否允许子类继承该注解的设置。默认为false。stateful
:是否状态保持,表示是否在每次执行任务时都保持状态。默认为false。jobName
:任务名称,用于指定计划任务的名称。如果未设置该参数,则默认使用方法名称作为任务名称。jobGroup
:任务组,用于将相关的计划任务分组在一起执行。如果未设置该参数,则默认使用方法所属的类作为任务组。
代码示例
Spring Boot提供了内置的@Scheduled
注解实现定时任务的功能。使用@Scheduled
注解创建定时任务非常简单,只需几行代码即可完成。
注意:默认情况下,Spring Boot定时任务是单线程方式执行的。
如果同一时刻有两个定时任务需要执行,那么只能在一个定时任务完成之后再执行下一个。
如果只有一个定时任务,这样做肯定没问题;当定时任务增多时,如果一个任务被阻塞,则会导致其他任务无法正常执行。要解决这个问题,需要配置任务调度线程池。
单线程定时任务
-
创建定时任务类
首先创建SchedulerTask类,然后在任务方法上添加@Scheduled注解,@EnableScheduling开启定时任务,具体的代码如下:
import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;import java.text.SimpleDateFormat; import java.util.Date; @EnableScheduling @Component @Slf4j public class SchedulerTask {@Scheduled(cron="*/10 * * * * ?")protected void taskCron(){SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");log.info("现在时间Scheduled1: {}" , dateFormat.format(new Date()));} }
-
启动项目
创建好SchedulerTask定时任务后启动项目,查看后台任务的运行情况,如图22-1所示。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication(scanBasePackages = "org.sea.example.day14.task") public class SpringbootDay14Application {public static void main(String[] args) {SpringApplication.run(SpringbootDay14Application.class, args);}}
图22-1 后台定时任务执行日志
后台日志显示,SchedulerTask任务每隔10秒输出当前时间,说明定义的任务正在后台定时执行
多线程定时任务
-
增加多线程配置类
增加SchedulerConfig配置类,代码如下:
import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor; @Configuration @Slf4j public class SchedulerConfig {@Beanpublic Executor asyncTaskScheduler() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.setMaxPoolSize(10);executor.setQueueCapacity(3);executor.initialize();log.info("多线程配置类初始化");return executor;} }
设置执行线程池为3,最大线程数为10。
-
SchedulerTask定时任务
在类上增加@EnableAsync注解,在方法上增加@Async注解,使得后台任务能够异步执行,代码如下:
mport lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;import java.text.SimpleDateFormat; import java.util.Date;@EnableAsync @EnableScheduling @Component @Slf4j public class SchedulerAsyncTask {@Async@Scheduled(cron="*/10 * * * * ?")protected void task1(){SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");log.info("现在时间Scheduled1: {}" , dateFormat.format(new Date()));}@Async@Scheduled(cron="*/10 * * * * ?")protected void task2(){SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");log.info("现在时间Scheduled2: {}" , dateFormat.format(new Date()));}@Async@Scheduled(cron="*/10 * * * * ?")protected void task3(){SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");log.info("现在时间Scheduled3: {}" , dateFormat.format(new Date()));} }
在上面的示例中,定时任务类SechedulerTask增加了@EnableAsync注解,开启了异步事件支持。同时,在定时方法上增加@Async注解,使任务能够异步执行,这样各个后台任务就不会阻塞。
-
启动项目
创建好SchedulerTask定时任务后启动项目,查看后台任务的运行情况,如图22-2所示。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication(scanBasePackages = "org.sea.example.day14.task2") public class SpringbootDay14Application {public static void main(String[] args) {SpringApplication.run(SpringbootDay14Application.class, args);}}
图22-2 后台定时任务执行日志
通过后台日志可以看到,Spring Boot启动线程池负责调度执行后台任务,各个后台任务之间相对独立、互不影响。
-
Quartz
1、什么是Quartz?
Quartz是OpenSymphony开源组织在任务调度(Job Scheduling,也称为作业调度)领域下的开源项目,它是Java开发的开源任务调度管理系统,具有使用灵活、配置简单的特点,能够实现复杂应用场景下的任务调度管理。当定时任务愈加复杂时,使用Spring Boot注解@Scheduled已经不能满足业务需要。相比之下,Quartz灵活而又不失简单,能够创建简单或复杂的调度任务,其主要具有如下功能:
1)持久化:将任务和状态持久化到数据库。
2)任务管理:对调度任务进行有效的管理。
3)集群:借助关系数据库和JDBC任务存储支持集群。
2、 Quartz的基本概念
Quartz是一个由Java开发的开源框架,用于执行定时任务。以下是Quartz的一些基本概念:
- Job:Job是一个接口,代表一个具体的任务。任务的具体逻辑在execute方法中实现。每次执行Job时,Quartz都会重新创建一个Job实例。
- JobDetail:用于定义Job的实例。因为相同的任务逻辑可能会被多次执行,所以使用JobDetail来创建每个独立的Job实例,确保各个任务可以独立运行。
- Trigger:Trigger是一个接口,用于定义执行给定Job的时间规则。主要有SimpleTrigger和CronTrigger这两个子接口。
- Scheduler:Scheduler是一个接口,代表Quartz的独立运行容器,用于与调度程序交互。
- JobBuilder:用于定义或构建JobDetail实例,帮助创建Job的实例。
- TriggerBuilder:用于定义或构建Trigger实例。
Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组和名称,组和名称是Scheduler查找、定位容器中某个对象的依据,Trigger的组和名称必须唯一,JobDetail的组和名称也必须唯一(但可以与Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组和名称访问控制容器中的Trigger与JobDetail。
总而言之,Scheduler相当于一个容器,其中包含各种Job和Trigger,四者之间的关系如图22-3所示
图22-3 四者之间的关系图
Quartz通过Scheduler触发Trigger规则实现任务的管理和调度。除此之外,Quartz还提供了TriggerBuilder
和JobBuilder
类来构建Trigger
实例和Job
实例。
Quartz主要有简单定时调度器(SimpleSchedule
)和Cron表达式调度器(CronSchedule
)来调度触发的定时任务。下面通过示例演示这两种调度器的用法。
简单定时调度器(SimpleSchedule
)
-
添加Quartz依赖
在pom.xml中配置Quartz的依赖包spring-boot-starter-quartz,具体配置如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>
-
创建任务
创建定时任务的实现类SimpleJob,并继承QuartzJobBean,示例代码如下:
import lombok.extern.slf4j.Slf4j; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean;@Slf4j public class SimpleJob extends QuartzJobBean {private String name;public void setName(String name) {this.name = name;}@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {log.info(String.format("Hello %s!", this.name));} }
-
构建JobDetail、CronTrigger
接下来构建JobDetail和Trigger实例。首先使用SimpleScheduleBuilder创建Scheduler实例,然后关联JobDetail和Trigger实例。示例代码如下:
import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class SimpleScheduler {@Beanpublic JobDetail simpleJobDetail() {return JobBuilder.newJob(SimpleJob.class).withIdentity("simpleJobDetail").usingJobData("name", "test simpleJob").storeDurably().build();}@Beanpublic Trigger sampleJobTrigger() {SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever();return TriggerBuilder.newTrigger().forJob(simpleJobDetail()).withIdentity("sampleJobTrigger").withSchedule(scheduleBuilder).build();} }
-
运行任务
启动项目,验证任务是否能正常运行。如图22-4所示,
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /**修改scanBasePackages */ @SpringBootApplication(scanBasePackages = "org.sea.example.day14.job") public class SpringbootDay14Application {public static void main(String[] args) {SpringApplication.run(SpringbootDay14Application.class, args);}}
图22-4 简单任务运行日志
SimpleJob后台任务成功运行,每隔10秒执行一次,这说明使用SimpleSchedule创建简单的定时任务运行成功。
Cron表达式调度器(CronSchedule
)
-
定义Job
import lombok.extern.slf4j.Slf4j; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean;@Slf4j public class CronJob extends QuartzJobBean {private String name;public void setName(String name) {this.name = name;}@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {log.info(String.format("Hello %s!", this.name));} }
-
构建JobDetail、CronTrigger
import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class CronScheduler {@Beanpublic JobDetail cronJobDetail() {return JobBuilder.newJob(CronJob.class).withIdentity("cronJobDetail").usingJobData("name", "test cronJob").storeDurably().build();}@Beanpublic Trigger cronJobTrigger() {CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ?");return TriggerBuilder.newTrigger().forJob(cronJobDetail()).withIdentity("cronJobTrigger").withSchedule(scheduleBuilder).build();} }
-
运行任务
启动项目,验证任务是否能正常运行。如图22-5所示,
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /**修改scanBasePackages */ @SpringBootApplication(scanBasePackages = "org.sea.example.day14.job2") public class SpringbootDay14Application {public static void main(String[] args) {SpringApplication.run(SpringbootDay14Application.class, args);}}
图22-5 Cron定时任务运行日志
CronJob后台任务成功运行,每隔10秒执行一次,这说明使用
CronScheduleBuilder
创建简单的定时任务运行成功。
相关文章:

第二十二章 : Spring Boot 集成定时任务(一)
第二十二章 : Spring Boot 集成定时任务(一) 前言 本章知识点: 介绍使用Spring Boot内置的Scheduled注解来实现定时任务-单线程和多线程;以及介绍Quartz定时任务调度框架:简单定时调度器(Simp…...

关于“Python”的核心知识点整理大全32
目录 12.6.4 调整飞船的速度 settings.py ship.py alien_invasion.py 12.6.5 限制飞船的活动范围 ship.py 12.6.6 重构 check_events() game_functions.py 12.7 简单回顾 12.7.1 alien_invasion.py 12.7.2 settings.py 12.7.3 game_functions.py 12.7.4 ship.py …...

【krita】实时绘画 入门到精通 海报+电商+装修+人物
安装插件 首先打开comfyUI,再打开krita,出现问题提示, 打开 cd custom_nodes 输入命令 安装控件 git clone https://github.com/Acly/comfyui-tooling-nodes.git krita基础设置 设置模型 设置lora (可设置lora强度 增加更多…...

云原生系列2-CICD持续集成部署-GitLab和Jenkins
1、CICD持续集成部署 传统软件开发流程: 1、项目经理分配模块开发任务给开发人员(项目经理-开发) 2、每个模块单独开发完毕(开发),单元测试(测试) 3、开发完毕后,集成部…...

50ms时延工业相机
华睿工业相机A3504CG000 参数配置: 相机端到端理论时延:80ms 厂家同步信息,此款设备帧率上线23fps,单帧时延:43.48ms,按照一图缓存加上传输显示的话,厂家预估时延在:80ms 厂家还有…...

CPU缓存一致性问题
什么是可见性问题? Further Reading :什么是可见性问题? 缓存一致性 内存一致性 内存可见性 顺序一致性区别 CPU缓存一致性问题 由于CPU缓存的出现,很好地解决了处理器与内存速度之间的矛盾,极大地提高了CPU的吞吐能…...
35道HTML高频题整理(附答案背诵版)
1、简述 HTML5 新特性 ? HTML5 是 HTML 的最新版本,它引入了很多新的特性和元素,以提供更丰富的网页内容和更好的用户体验。以下是一些主要的新特性: 语义元素:HTML5 引入了新的语义元素,像 <article&g…...

【powershell】Windows环境powershell 运维之历史文件压缩清理
🦄 个人主页——🎐开着拖拉机回家_Linux,大数据运维-CSDN博客 🎐✨🍁 🪁🍁🪁🍁🪁🍁🪁🍁 🪁🍁🪁&am…...

【Linux】Linux线程概念和线程控制
文章目录 一、Linux线程概念1.什么是线程2.线程的优缺点3.线程异常4.线程用途5.Linux进程VS线程 二、线程控制1.线程创建2.线程终止3.线程等待4.线程分离 一、Linux线程概念 1.什么是线程 线程是进程内的一个执行流。 我们知道,一个进程会有对应的PCB,…...

Flink cdc3.0同步实例(动态变更表结构、分库分表同步)
文章目录 前言准备flink环境docker构建mysql、doris环境数据准备 通过 FlinkCDC cli 提交任务整库同步同步变更路由变更路由表结构不一致无法同步 结尾 前言 最近Flink CDC 3.0发布, 不仅提供基础的数据同步能力。schema 变更自动同步、整库同步、分库分表等增强功…...

国产Apple Find My认证芯片哪里找,伦茨科技ST17H6x芯片可以帮到您
深圳市伦茨科技有限公司(以下简称“伦茨科技”)发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家,该平台提供可通过Apple Find My认证的Apple查找(Find My)功能集成解决方案。…...
肺癌相关知识
写在前面 大概想了解下肺癌相关的知识,开此贴做记录,看看后续有没有相关的生信文章思路。 综述 文章名期刊影响因子Lung cancer immunotherapy: progress, pitfalls, and promisesMol Cancer37.3 常见治疗手段有surgery, radiation therapy, chemoth…...

ChimeraX使用教程-安装及基本操作
ChimeraX使用教程-安装及基本操作 1、访问https://www.cgl.ucsf.edu/chimerax/download.html进行下载,然后安装 安装完成后,显示界面 2、基本操作 1、点击file,导入 .PDB 文件。 (注:在 alphafold在线预测蛋白》点…...

【小黑嵌入式系统第十一课】μC/OS-III程序设计基础(一)——任务设计、任务管理(创建基本状态内部任务)、任务调度、系统函数
上一课: 【小黑嵌入式系统第十课】μC/OS-III概况——实时操作系统的特点、基本概念(内核&任务&中断)、与硬件的关系&实现 文章目录 一、任务设计1.1 任务概述1.2 任务的类型1.2.1 单次执行类任务(运行至完成型&#…...

Redis一些常用的技术
文章目录 第1关:Redis 事务与锁机制第2关:流水线第3关:发布订阅第4关:超时命令第5关:使用Lua语言 第1关:Redis 事务与锁机制 编程要求 根据提示,在右侧编辑器Begin-End补充代码,根据…...
基于QPainter 绘图图片绕绘制设备中心旋转
项目地址:https://gitcode.com/m0_45463480/QPainter/tree/main 获取途径:进入CSDN->GitCode直接下载或者通过git拉取仓库内容。 QPainter是Qt框架中的一个类,用于在QWidget或QPixmap等设备上进行绘图操作。它提供了丰富的绘图功能,可以用于绘制线条、图形、文本等。Q…...

计算机网络(4):网络层
网络层提供的两种服务 虚电路服务(Virtual Circuit Service)和数据报服务(Datagram Service)是在网络层(第三层)提供的两种不同的通信服务。它们主要区别在于建立连接的方式和数据传输的方式。 虚电路服务…...

动态内存分配(malloc和free、calloc和realloc)
目录 一、为什么要有动态内存分配 二、C/C中程序内存区域划分 三、malloc和free 2.1、malloc 2.2、free 四、calloc和realloc 3.1、calloc 3.2、realloc 3.3realloc在调整内存空间的是存在两种情况: 3.4realloc有malloc的功能 五、常见的动…...

C语言---井字棋(三子棋)
Tic-Tac-Toe 1 游戏介绍和随机数1.1 游戏介绍1.2 随机数的生成1.3 棋盘大小和符号 2 设计游戏2.1 初始化棋盘2.2 打印棋盘2.3 玩家下棋2.4 电脑下棋2.5 判断输赢2.6 game()函数2.7 main()函数 3 完整三子棋代码3.1 Tic_Tac_Toe.h3.2 Tic_Tac_Toe.c3.3 Test.c 4 游戏代码的缺陷 …...

[Kubernetes]3. k8s集群Service详解
在上一节讲解了k8s 的pod,deployment,以及借助pod,deployment来部署项目,但会存在问题: 每次只能访问一个 pod,没有负载均衡自动转发到不同 pod访问还需要端口转发Pod重创后IP变了,名字也变了针对上面的问题,可以借助Service来解决,下面就来看看Service怎么使用 一.Service详…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...