当前位置: 首页 > news >正文

调度器怎么自己写?调度器在实现时需要注意哪些细节?请写一个jvm的调度器?如何在这个调度器中添加多个任务?

如果你想自己编写一个调度器,可以按照以下步骤进行:
一、确定需求和目标
明确调度器的应用场景,例如任务调度、资源分配、进程管理等。
确定调度的对象,比如任务、作业、进程等。
定义调度的目标,如最小化完成时间、最大化资源利用率、保证公平性等。
二、设计数据结构
任务队列:用于存储待调度的任务,可以使用数组、链表或优先队列等数据结构。
任务结构体:包含任务的属性,如任务 ID、优先级、执行时间、所需资源等。
资源结构体:描述可用资源的情况,如 CPU 核心数、内存容量、磁盘空间等。
三、实现调度算法
先来先服务(FCFS):按照任务到达的顺序进行调度,先到达的任务先执行。
维护一个任务队列,新任务加入队列尾部。
从队列头部取出任务分配资源并执行。
短作业优先(SJF):优先调度执行时间最短的任务。
维护一个按照任务执行时间排序的任务队列。
新任务加入队列时,根据执行时间进行插入排序。
选择执行时间最短的任务执行。
时间片轮转(RR):将时间划分为固定大小的时间片,每个任务轮流执行一个时间片。
维护一个任务队列,新任务加入队列尾部。
每个任务执行一个时间片后,回到队列尾部等待下一轮调度。
优先级调度:根据任务的优先级进行调度,优先级高的任务先执行。
维护一个按照优先级排序的任务队列。
新任务加入队列时,根据优先级进行插入排序。
选择优先级最高的任务执行。
四、资源管理
资源分配:当任务被调度执行时,分配所需的资源给任务。
检查可用资源是否满足任务需求,如果满足则分配资源,否则将任务放入等待队列。
资源回收:当任务完成执行后,回收分配给任务的资源。
更新资源结构体,标记资源为可用状态。
五、错误处理和优化
错误处理:考虑任务失败、资源不足、调度异常等情况,进行适当的错误处理和恢复机制。
优化:根据实际需求进行优化,如减少调度开销、提高资源利用率、动态调整调度策略等。
以下是一个简单的 Python 示例代码,实现了一个先来先服务的调度器:

class Task:def __init__(self, id, execution_time):self.id = idself.execution_time = execution_timeclass Scheduler:def __init__(self):self.task_queue = []def add_task(self, task):self.task_queue.append(task)def run(self):while self.task_queue:task = self.task_queue.pop(0)print(f"Executing task {task.id}")# 模拟任务执行import timetime.sleep(task.execution_time)

你可以根据自己的需求对这个示例进行扩展和修改,实现更复杂的调度功能。同时,不同的编程语言和应用场景可能需要不同的实现方式,但基本的原理是相似的。
在实现调度器时,需要注意以下细节:
一、任务管理方面
任务状态跟踪:
准确记录任务的不同状态,如待调度、正在执行、暂停、完成等。这有助于及时了解任务的进展情况,并进行合理的调度决策。
当任务状态发生变化时,及时更新相关的数据结构和状态标志,以便调度器能够做出正确的反应。
任务优先级处理:
如果调度器支持任务优先级,要确保高优先级任务能够在合适的时机得到优先执行。这可能涉及到在任务队列中快速定位高优先级任务,以及在资源分配时优先考虑高优先级任务的需求。
处理优先级反转问题,即低优先级任务持有高优先级任务所需的资源,导致高优先级任务被阻塞。可以采用优先级继承等技术来解决这个问题。
任务依赖关系:
如果任务之间存在依赖关系,调度器需要能够识别这些依赖关系,并确保被依赖的任务先执行完成。这可能需要构建任务依赖图或使用其他数据结构来表示任务之间的关系。
在调度过程中,要及时检测任务依赖关系是否满足,避免出现因依赖关系不满足而导致任务无法执行的情况。
二、资源管理方面
资源分配与回收:
精确地分配资源给任务,确保资源的分配符合任务的需求,并且不会出现资源过度分配导致系统崩溃的情况。
在任务完成执行后,及时回收资源,以便其他任务可以使用。这需要对资源的使用情况进行实时监测,并在合适的时候进行资源回收操作。
资源冲突解决:
当多个任务同时请求相同的资源时,需要有合理的冲突解决机制。可以采用资源锁定、排队等待、资源共享等方式来处理资源冲突。
避免死锁的发生,即两个或多个任务相互等待对方释放资源,导致系统陷入停滞状态。可以通过资源分配策略、死锁检测和恢复机制来防止死锁。
资源动态调整:
考虑系统资源的动态变化情况,如资源的增加或减少。调度器应该能够适应资源的变化,并相应地调整任务的调度策略。
例如,如果系统中增加了新的资源,可以重新评估任务的资源需求,并进行更优化的资源分配。
三、性能优化方面
调度算法效率:
选择高效的调度算法,以减少调度的时间开销。不同的调度算法在不同的场景下性能表现不同,需要根据实际需求进行选择和优化。
对于大规模任务或高并发场景,要考虑算法的可扩展性,确保调度器能够在合理的时间内完成任务调度。
数据结构选择:
选择合适的数据结构来存储任务队列、资源信息等。例如,使用优先队列可以快速获取高优先级任务,使用哈希表可以快速查找任务的状态和资源使用情况。
数据结构的选择应该考虑到操作的效率和空间复杂度,以提高调度器的整体性能。
并发与并行处理:
如果调度器需要处理多个任务的并发执行,可以考虑使用多线程、多进程或异步编程技术来提高系统的并发性。
合理地分配任务到不同的执行单元,充分利用系统的多核处理器或分布式计算资源,提高任务的执行效率。
四、错误处理方面
异常情况处理:
考虑各种可能的异常情况,如任务执行失败、资源不可用、系统故障等。调度器应该能够捕获这些异常,并采取适当的措施进行处理,如重新调度任务、释放资源、记录错误日志等。
提供清晰的错误信息和日志记录,以便在出现问题时进行故障排查和分析。
容错机制:
设计容错机制,使调度器能够在部分任务或资源出现问题的情况下继续运行。例如,可以采用备份任务、冗余资源等方式来提高系统的可靠性。
对于关键任务,可以考虑设置重试机制或备份执行路径,以确保任务的成功执行。
性能监测与调优:
建立性能监测机制,实时监测调度器的性能指标,如任务完成时间、资源利用率、调度延迟等。根据监测结果进行性能调优,调整调度参数和算法,以提高系统的整体性能。
定期对调度器进行性能测试和评估,发现潜在的性能瓶颈,并进行针对性的优化。
编写一个简单的 JVM 调度器是一个复杂的任务,因为 JVM 本身已经包含了线程调度机制。不过,我们可以模拟一个简单的任务调度器,来管理和调度一组任务。这可以通过使用 Java 的线程池来实现。

下面是一个简单的 Java 程序,使用 ScheduledExecutorService 来调度任务:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class SimpleScheduler {public static void main(String[] args) {// 创建一个调度线程池ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);// 定义一个任务Runnable task = () -> System.out.println("任务执行时间: " + System.currentTimeMillis());// 以固定的速率调度任务scheduler.scheduleAtFixedRate(task, 0, 5, TimeUnit.SECONDS);// 添加一个关闭钩子来优雅地关闭调度器Runtime.getRuntime().addShutdownHook(new Thread(() -> {System.out.println("关闭调度器...");scheduler.shutdown();try {if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {scheduler.shutdownNow();}} catch (InterruptedException e) {scheduler.shutdownNow();}}));}
}

代码解释:

  • ScheduledExecutorService: 用于调度任务的执行。
  • scheduleAtFixedRate: 以固定的速率执行任务。
  • Shutdown Hook: 确保在程序终止时优雅地关闭调度器。

这个简单的调度器将在程序启动后每隔 5 秒执行一次任务,并在程序结束时关闭调度器。你可以根据需要调整任务的执行间隔和线程池的大小。
你可以在调度器中添加多个任务,只需为每个任务调用 scheduleAtFixedRatescheduleWithFixedDelay 方法。以下是如何实现的示例:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class MultiTaskScheduler {public static void main(String[] args) {// 创建一个调度线程池ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);// 定义第一个任务Runnable task1 = () -> System.out.println("任务1执行时间: " + System.currentTimeMillis());// 定义第二个任务Runnable task2 = () -> System.out.println("任务2执行时间: " + System.currentTimeMillis());// 以固定的速率调度第一个任务scheduler.scheduleAtFixedRate(task1, 0, 5, TimeUnit.SECONDS);// 以固定的延迟调度第二个任务scheduler.scheduleWithFixedDelay(task2, 0, 10, TimeUnit.SECONDS);// 添加一个关闭钩子来优雅地关闭调度器Runtime.getRuntime().addShutdownHook(new Thread(() -> {System.out.println("关闭调度器...");scheduler.shutdown();try {if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {scheduler.shutdownNow();}} catch (InterruptedException e) {scheduler.shutdownNow();}}));}
}

代码说明:

  • task1task2 是两个不同的任务。
  • scheduleAtFixedRate(task1, 0, 5, TimeUnit.SECONDS): 每 5 秒执行一次 task1
  • scheduleWithFixedDelay(task2, 0, 10, TimeUnit.SECONDS): 每次 task2 执行完后等待 10 秒再执行下一次。
  • 你可以根据需要增加更多的任务,并指定不同的调度策略。
  • 选择使用 Future 还是 CompletableFuture 取决于你的具体需求和场景。以下是一些考虑因素:

使用 Future 的情况

  • 简单任务:如果只需要执行一个简单的异步任务并获取结果,Future 可以满足需求。
  • 阻塞获取结果:如果你不介意在获取结果时阻塞线程,Future.get() 是简单直接的。
  • 无需复杂的任务链:如果没有需要处理的一系列异步任务或依赖关系。

使用 CompletableFuture 的情况

  • 复杂异步流程CompletableFuture 支持构建复杂的异步任务链,可以轻松处理多个任务的依赖关系。
  • 非阻塞操作:提供了多种非阻塞方法,如 thenApply, thenAccept, thenCompose,允许在结果可用时进行进一步处理。
  • 并行任务:支持并行执行多个任务并组合它们的结果,例如使用 allOfanyOf
  • 增强的异常处理:提供了更丰富的异常处理机制,比如 exceptionallyhandle

示例场景

  • Future 示例:获取一个任务的结果,可能需要阻塞等待。

    Future<String> future = executorService.submit(callableTask);
    try {String result = future.get(); // 阻塞等待结果
    } catch (InterruptedException | ExecutionException e) {e.printStackTrace();
    }
    
  • CompletableFuture 示例:链式处理多个任务。

    CompletableFuture.supplyAsync(() -> {return "任务1结果";
    }).thenApply(result -> {return result + " -> 任务2结果";
    }).thenAccept(finalResult -> {System.out.println("最终结果: " + finalResult);
    });
    

总之,CompletableFuture 提供了更高的灵活性和功能,对于复杂的异步操作和任务依赖更为合适,而 Future 更适合简单的异步操作。

在使用 CompletableFuture 时,可以通过多种方式处理异常情况。以下是一些常用的方法:

使用 exceptionally

exceptionally 方法允许你在任务抛出异常时提供一个默认值:

CompletableFuture.supplyAsync(() -> {if (Math.random() > 0.5) {throw new RuntimeException("任务失败");}return "成功结果";
}).exceptionally(ex -> {System.out.println("异常: " + ex.getMessage());return "默认结果";
}).thenAccept(result -> {System.out.println("最终结果: " + result);
});

使用 handle

handle 方法允许你处理正常结果和异常情况:

CompletableFuture.supplyAsync(() -> {if (Math.random() > 0.5) {throw new RuntimeException("任务失败");}return "成功结果";
}).handle((result, ex) -> {if (ex != null) {System.out.println("异常: " + ex.getMessage());return "处理后的默认结果";}return result;
}).thenAccept(result -> {System.out.println("最终结果: " + result);
});

使用 whenComplete

whenComplete 方法在任务完成后执行,无论是否有异常:

CompletableFuture.supplyAsync(() -> {if (Math.random() > 0.5) {throw new RuntimeException("任务失败");}return "成功结果";
}).whenComplete((result, ex) -> {if (ex != null) {System.out.println("异常: " + ex.getMessage());} else {System.out.println("结果: " + result);}
});

总结

  • exceptionally: 用于处理异常并返回默认值。
  • handle: 处理结果和异常,返回新的结果。
  • whenComplete: 查看结果或异常,不改变结果。

选择哪种方法取决于你的具体需求和对异常的处理方式。

相关文章:

调度器怎么自己写?调度器在实现时需要注意哪些细节?请写一个jvm的调度器?如何在这个调度器中添加多个任务?

如果你想自己编写一个调度器&#xff0c;可以按照以下步骤进行&#xff1a; 一、确定需求和目标 明确调度器的应用场景&#xff0c;例如任务调度、资源分配、进程管理等。 确定调度的对象&#xff0c;比如任务、作业、进程等。 定义调度的目标&#xff0c;如最小化完成时间、最…...

创客匠人对话|德国临床营养学家单场发售百万秘笈大公开

老蒋创客圈第66期对话标杆直播连麦&#xff0c;我们邀请到【梦想身型健康管理学院】平台创始人吴迪老师。为我们分享“健康管理赛道单场发售破百万&#xff01;创始人背后的操盘秘笈是什么&#xff1f;”&#xff0c;深度剖析如何去展示自己的核心竞争力&#xff1f;如何扩大专…...

开源项目低代码表单FormCreate从Vue2到Vue3升级指南

开源项目低代码表单 FormCreate v3 版本基于 Vue 3.0 构建&#xff0c;尽管功能与 v2 版本大致相同&#xff0c;但有一些重要的变更和不兼容项需要注意。 源码地址: Github | Gitee FormCreate v3 对比 v2 版本在一些功能和配置项上做了调整&#xff0c;以更好地支持 Vue 3 的…...

序偶解释:李冬梅老师书线性表一章第一页

序偶的定义&#xff1a; 有序偶是两个对象的搜集&#xff0c;使得可以区分出其中一个是“第一个元素”而另一个是“第二个元素”。带有第一个元素a和第二个元素b的有序偶通常写为(a,b)。例如&#xff0c;在数学中&#xff0c;有序偶用于表示二维空间上的点。序偶的特性&#xf…...

3GPP协议入门——物理层基础(二)

物理层基础&#xff08;一&#xff09;在这里~ 物理层基础&#xff08;一&#xff09; 1.RE Resource Element&#xff0c;NR中最小的资源单位&#xff0c;时域上是一个OFDM符号长度&#xff0c;频域上为一个子载波宽度。 2. RB Resource Block&#xff0c;时域上是一个OFDM符…...

Java学习Day41:手刃青背龙!(spring框架之事务)

1.spring事务概念 在数据层和业务层保证一系列数据库操作原子性成功失败&#xff01;&#xff08;相比事务可以在业务层开启&#xff09; 1.事务定义&#xff1a;关键字&#xff1a;Transactional&#xff08;一般写在接口上&#xff09; 2.事务管理器&#xff1a;在JdbcCon…...

el-image(vue 总)

一 加载静态资源 在第一次使用vue3开发项目时&#xff0c;使用require&#xff08;‘图片路径’&#xff09;&#xff0c;结果浏览器报错&#xff1a; Uncaught (in promise) ReferenceError: require is not defined 因为require是webpack提供的一种加载能力&#xff0c;但…...

餐饮「收尸人」,血亏奶茶店……

最近一段时间&#xff0c;小柴朋友圈叫苦的餐饮人是越来越多了&#xff01; 比如某天早上睡醒查看朋友圈奏折的时候&#xff0c;有个以前经常光顾的餐馆的老板&#xff0c;发了一条朋友圈&#xff1a;最终&#xff0c;还是要和自己经营了11年的小店告别了……‍‍ 配的照片是店…...

【Python进阶】学习Python从入门到进阶,详细步骤,就看这一篇。文末附带项目演练!!!

详细的Python学习路线 1. Python基础 Python安装和环境配置&#xff1a;学习如何在你的操作系统上安装Python&#xff0c;并配置开发环境。变量和数据类型&#xff1a;学习如何定义变量&#xff0c;以及Python中的基本数据类型&#xff0c;如整数、浮点数、字符串等。 Pytho…...

OpenCV结构分析与形状描述符(9)检测轮廓相对于其凸包的凹陷缺陷函数convexityDefects()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 查找一个轮廓的凸性缺陷。 下图显示了一个手部轮廓的凸性缺陷&#xff1a; convexityDefects 是 OpenCV 库中的一个函数&#xff0c;用于检测轮…...

HTTP 之 响应头信息(二十三)

应答头说明Allow服务器支持哪些请求方法&#xff08;如GET、POST等&#xff09;。Content-Encoding文档的编码&#xff08;Encode&#xff09;方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutp…...

智能风扇的全新升级:NRK3603语音芯片识别控制模块的应用

在当今智能化生活的潮流中&#xff0c;如何让家电更加人性化、便捷化&#xff0c;已经成为消费者和制造商关注的焦点。在这股大潮中&#xff0c;NRK3603语音识别模块以其出色的性能和广泛的应用&#xff0c;为智能电风扇带来了全新的升级。 1. 芯片特性 NRK3603是一款高性能、…...

如何通过pSLC技术实现性能与容量的双赢

目录 一、什么是 pSLC 二、各 NAND FLASH 的特点 三、pSLC 的优缺点 四、应用场景 一、什么是 pSLC pSLC&#xff08;Pseudo-Single Level Cell&#xff09;即伪 SLC&#xff0c;是一种将 MLC/TLC 改为 SLC 的一种技术&#xff0c;现 Nand Flash 基本支持此功能&#xff0…...

减速电机的基本结构及用料简介

资料来源&#xff1a;淘宝上某商家&#xff0c;它提供功率极小的电机。比如5W 1.整体结构剖面图 如下图&#xff0c;左侧是减速箱&#xff0c;和动力输出轴&#xff0c;右侧可以看到定子的铜丝绕组和中间的转子&#xff0c;它们贴合地非常紧密。气隙很窄。它的转子很像是铝制…...

1688跨境电商接口开放接入,跨境电商的尽头到底谁在赚钱?

1688&#xff08;阿里巴巴&#xff09;作为国内甚至全世界最大的线上批发集采平台&#xff0c;一直以来都是大部分跨境卖家选品和开发的主要平台之一&#xff0c;也保持着良好的上下游供应链关系。 不过&#xff0c;就在近日&#xff0c;1688正式推出跨境寻源通计划&#xff0c…...

SpringBoot 增量部署发布

一、背景介绍 由于项目依赖的jar越来越多&#xff0c;Springboot默认的打包方式是将整个项目打包成一个jar包&#xff0c;每次发布时&#xff0c;打包后的jar越来越大&#xff0c;更新一个很小的功能&#xff0c;需要将整个jar上传运行。这样效率太低了&#xff0c;考虑实现每…...

java八股!1

集合 目录 集合java中存在哪些集合&#xff1f;底层实现逻辑&#xff1f;哪些集合是线程安全的&#xff1f;集合的对比&#xff1a;hash冲突如何解决hashmap为什么线程不安全&#xff0c;如何实现安全&#xff1f;hashmap中循环链表的产生hashmap底层实现原理和扩容机制map的遍…...

【学术会议征稿】2024年智能驾驶与智慧交通国际学术会议(IDST 2024)

2024年智能驾驶与智慧交通国际学术会议(IDST 2024) 2024 International Conference on Intelligent Driving and Smart Transportation 智能驾驶和智慧交通利用新兴技术&#xff0c;使城市出行更加方便、更具成本效益且更安全。在此背景下&#xff0c;由中南大学主办的2024年…...

2024最全网络安全工程师面试题(附答案)

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 2024年过去了一大半&#xff0c;先来灵魂三连问&#xff0c;年初定的目标完成多少了&#xff1f;薪资涨了吗&#xff1f;女朋友找到了吗&#xff1f; 一、网络…...

828华为云征文| 华为云 Flexus X 实例:引领云计算新时代的柔性算力先锋

828华为云征文| 华为云 Flexus X 实例&#xff1a;引领云计算新时代的柔性算力先锋 1. 推出背景 在数字经济快速发展的当下&#xff0c;数字化转型已成为企业发展的必然趋势。然而&#xff0c;中小企业在数字化转型过程中面临着诸多困境。据《2024 年中小企业数字化转型白皮书…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

【WebSocket】SpringBoot项目中使用WebSocket

1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖&#xff0c;添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...