苍穹外卖笔记Day09Day10
1. SpringTask 定时任务(苍穹外卖实战)
1.1 基本概念
SpringTask 是 Spring 框架原生自带 的定时任务工具,无需整合 Quartz 等第三方中间件,轻量、简单、开箱即用,专门用于处理苍穹外卖中「周期性、定时执行」的业务场景,无需手动触发,自动按配置时间执行。
1.2 核心作用(苍穹外卖实战场景)
- 定时清理:定时清理过期未支付订单(如 30 分钟未支付自动取消)、清理过期优惠券、清理日志数据;
- 定时统计:每日凌晨统计前一天的订单量、营业额、用户新增数量,生成运营报表;
- 定时同步:定时同步商品库存、同步用户积分、同步配送状态;
- 定时提醒:定时提醒商家处理待接单订单、提醒配送员取餐。
1.3 核心注解(必背,面试高频)
(1)@EnableScheduling
-
作用:开启 SpringTask 定时任务功能,必须加在 SpringBoot 启动类 或 配置类 上,否则定时任务不生效。
-
苍穹外卖实战示例(启动类):
java@SpringBootApplication @EnableScheduling // 开启定时任务 public class SkyApplication { public static void main(String[] args) { SpringApplication.run(SkyApplication.class, args); } }
(2)@Scheduled
-
作用:标记在「要定时执行的方法」上,指定任务的执行时间、频率。
-
方法要求:无参数、无返回值(void),否则定时任务会报错。
-
苍穹外卖实战示例(定时取消过期订单):
java@Service public class OrderServiceImpl implements OrderService { // 定时执行:每1分钟检查一次,取消30分钟未支付的订单 @Scheduled(cron = "0 */1 * * * ?") public void cancelExpiredOrder() { // 业务逻辑:查询30分钟未支付、未取消的订单,执行取消操作 List<Order> expiredOrders = orderMapper.selectExpiredUnpaidOrders(); for (Order order : expiredOrders) { order.setStatus(OrderStatus.CANCELLED); order.setCancelReason("订单超时未支付,自动取消"); orderMapper.update(order); } } }
1.4 @Scheduled 三大执行方式(面试重点,区分清楚)
(1)fixedRate:固定频率执行(不推荐用于苍穹外卖核心场景)
- 核心逻辑:从上一次任务开始执行的时间 计算,每隔指定时间执行一次,无论上一次任务是否执行完毕。
- 示例:
@Scheduled(fixedRate = 5000)→ 每 5 秒执行一次,即使上一次任务执行了 3 秒,下一次也会在第 5 秒准时启动。 - 缺点:如果任务执行时间超过设定频率,会导致任务堆积,占用线程资源,苍穹外卖中很少使用(除非是轻量、快速执行的任务)。
(2)fixedDelay:固定间隔执行(苍穹外卖常用)
- 核心逻辑:从上一次任务执行结束的时间 计算,等待指定时间后,再执行下一次任务,避免任务堆积。
- 示例:
@Scheduled(fixedDelay = 5000)→ 上一次任务执行完后,等待 5 秒,再执行下一次。 - 适用场景:苍穹外卖中耗时较短的定时任务(如每 10 分钟同步一次库存)。
(3)cron 表达式:精准定时(最常用、面试必问)
- 核心逻辑:通过表达式,精准控制任务的执行时间(支持秒、分、时、日、月、周、年),功能最强大,适配苍穹外卖中所有复杂定时场景。
- 语法(7个字段,空格分隔):
秒 分 时 日 月 周 年(可选)- 通配符说明(面试常考):
*:匹配所有值(如秒位写 *,表示每秒执行);?:仅用于「日」和「周」,表示不指定值(避免日和周冲突);/:表示递增(如 0/5 * * * * ? → 每 5 秒执行一次);-:表示范围(如 0 0 9-18 * * ? → 每天 9 点到 18 点,整点执行);,:表示多个值(如 0 0 12,18 * * ? → 每天 12 点、18 点各执行一次)。
- 通配符说明(面试常考):
- 苍穹外卖常用 Cron 示例(面试必背):
0 0 2 * * ?→ 每天凌晨 2 点(执行日志清理、报表统计);0 */1 * * * ?→ 每 1 分钟(检查过期订单);0 0 0 * * ?→ 每天凌晨 0 点(重置当日数据统计);0 0 12 * * ?→ 每天中午 12 点(推送当日订单统计给商家)。
1.5 SpringTask 执行机制(面试高频,坑点)
(1)默认机制
SpringTask 默认是 单线程执行,所有定时任务共用一个线程;如果一个任务执行时间过长(如清理大量日志耗时 5 分钟),会导致其他定时任务延迟执行,甚至卡死。
(2)多线程配置(生产环境必须配置,苍穹外卖实战)
解决单线程阻塞问题,配置线程池,让不同任务在不同线程中执行,互不影响。
-
配置类代码(面试可直接写):
java@Configuration @EnableScheduling public class ScheduledConfig implements SchedulingConfigurer { // 配置线程池,核心线程数根据任务数量调整(苍穹外卖建议5-10个) @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5)); } } -
面试考点:为什么要配置多线程?答:避免单线程任务阻塞,保证所有定时任务按时执行,提升系统稳定性。
1.6 面试高频问题(结合苍穹外卖)
- 苍穹外卖中,SpringTask 用在哪些场景?(答:清理过期订单、统计报表、同步库存、定时提醒);
- @EnableScheduling 和 @Scheduled 的作用分别是什么?(答:前者开启定时任务功能,后者标记定时方法);
- fixedRate 和 fixedDelay 的区别?(答:前者从任务开始计时,后者从任务结束计时,后者避免堆积);
- SpringTask 默认是单线程还是多线程?如何优化?(答:默认单线程,配置线程池优化);
- 写出一个"每天凌晨 2 点执行"的 Cron 表达式?(答:0 0 2 * * ?)。
2. WebSocket 详细讲解(苍穹外卖实战)
2.1 基本概念
WebSocket 是一种 全双工、双向通信 的网络协议,基于 TCP 连接,允许客户端(浏览器/APP)和服务器之间建立持久连接,实现「实时通信」------ 服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送消息,无需客户端频繁发起请求(区别于 HTTP 单向通信)。
2.2 核心作用(区别于 HTTP,面试重点)
HTTP 协议是「请求-响应」模式:客户端发起请求,服务器才会返回响应,服务器无法主动向客户端推送消息;
WebSocket 协议是「持久连接」模式:连接建立后,双方可随时互发消息,实时性极高,无需频繁请求,减少网络开销。
2.3 苍穹外卖实战场景(除了实时提醒新订单)
(1)核心场景1:新订单实时提醒(基础)
- 商家端:用户下单后,服务器通过 WebSocket 实时向商家推送新订单消息(无需商家刷新页面);
- 配送端:商家接单后,实时向配送员推送待取餐订单,同步订单地址、取餐时间。
(2)核心场景2:订单状态实时同步(高频)
- 用户端:用户下单后,实时同步订单状态(待接单 → 待配送 → 配送中 → 已完成),无需刷新页面;
- 商家端:实时同步订单支付状态(未支付 → 已支付)、取消状态,及时处理订单。
(3)核心场景3:实时聊天(苍穹外卖增值功能)
- 用户与商家聊天:用户咨询菜品、修改订单,商家实时回复;
- 用户与配送员聊天:用户询问配送进度,配送员实时反馈。
(4)核心场景4:库存实时更新(秒杀/热销场景)
- 商家端:热销菜品库存不足时,实时推送库存预警(如"番茄炒蛋库存仅剩5份");
- 用户端:用户下单时,实时推送库存变化(如"库存不足,无法下单")。
(5)核心场景5:实时通知(运营/系统通知)
- 商家端:平台推送活动通知(如"周末满减活动开启")、违规提醒、系统维护通知;
- 配送端:推送配送范围调整、配送费调整通知;
- 用户端:推送优惠券到账、会员权益更新、订单评价提醒。
(6)核心场景6:实时数据统计(商家后台)
- 商家端实时查看当日订单量、营业额、接单率,数据随订单变化实时更新,无需手动刷新报表。
2.4 WebSocket 核心原理(面试必背)
- 握手阶段:客户端发起 HTTP 请求,请求头中携带
Upgrade: websocket,表示要升级为 WebSocket 协议; - 连接建立:服务器响应同意升级,双方建立持久 TCP 连接,后续通信不再使用 HTTP 协议;
- 数据传输:连接建立后,客户端和服务器可通过帧(Frame)互发消息,支持文本、二进制数据;
- 连接关闭:客户端或服务器主动发送关闭帧,释放连接。
2.5 苍穹外卖 WebSocket 核心实现(简化版,面试可写)
(1)引入依赖(SpringBoot 整合 WebSocket)
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
(2)配置类(开启 WebSocket 支持)
java
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
// 注册 WebSocket 端点,开启 WebSocket 支持
return new ServerEndpointExporter();
}
}
(3)WebSocket 核心服务类(处理连接、消息推送)
java
// 端点路径:ws://localhost:8080/ws/order/{merchantId}(商家端,按商家ID区分连接)
@ServerEndpoint("/ws/order/{merchantId}")
@Component
public class OrderWebSocketServer {
// 存储商家ID与对应的WebSocket连接(key:商家ID,value:连接对象)
private static Map<Long, Session> sessionMap = new ConcurrentHashMap<>();
// 连接建立时执行(商家打开后台页面,建立连接)
@OnOpen
public void onOpen(Session session, @PathParam("merchantId") Long merchantId) {
sessionMap.put(merchantId, session);
}
// 连接关闭时执行(商家关闭页面,释放连接)
@OnClose
public void onClose(@PathParam("merchantId") Long merchantId) {
sessionMap.remove(merchantId);
}
// 接收客户端消息(商家发送消息给服务器)
@OnMessage
public void onMessage(String message, @PathParam("merchantId") Long merchantId) {
// 处理商家消息(如确认接单、拒绝订单)
System.out.println("收到商家" + merchantId + "的消息:" + message);
}
// 核心方法:服务器向指定商家推送消息(如新订单提醒)
public void sendMessage(Long merchantId, String message) {
Session session = sessionMap.get(merchantId);
if (session != null && session.isOpen()) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(4)业务层调用(推送新订单消息)
java
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderWebSocketServer webSocketServer;
// 下单成功后,向商家推送新订单消息
@Override
public void createOrder(OrderDTO orderDTO) {
// 1. 生成订单、入库等业务逻辑...
// 2. 向对应商家推送新订单消息
Long merchantId = orderDTO.getMerchantId();
String message = "您有新订单,请及时处理!订单号:" + orderDTO.getOrderNumber();
webSocketServer.sendMessage(merchantId, message);
}
}
2.6 面试高频问题(结合苍穹外卖)
- WebSocket 和 HTTP 的区别?(答:HTTP 单向请求-响应,无法主动推送;WebSocket 双向全双工,持久连接,实时性高,减少请求开销);
- 苍穹外卖中,WebSocket 除了新订单提醒,还有哪些用途?(答:订单状态同步、实时聊天、库存预警、实时通知、数据统计);
- WebSocket 连接建立的过程?(答:客户端发起 HTTP 升级请求 → 服务器响应升级 → 建立 TCP 持久连接);
- 如何保证 WebSocket 连接的稳定性?(答:心跳检测、重连机制、异常关闭时释放连接);
- 苍穹外卖中,如何区分不同商家的 WebSocket 连接?(答:通过商家ID作为key,存储连接对象,推送消息时根据商家ID定位连接)。
3. 总结(面试重点)
- SpringTask:Spring 原生定时工具,核心注解 @EnableScheduling + @Scheduled,三大执行方式(fixedRate、fixedDelay、cron),必须配置多线程避免阻塞,苍穹外卖主要用于定时清理、统计、同步任务;
- WebSocket:双向实时通信协议,核心是持久连接,苍穹外卖除新订单提醒,还用于订单状态同步、实时聊天、库存预警等场景,核心实现是端点配置 + 连接管理 + 消息推送;
- 两者关联:SpringTask 可定时触发 WebSocket 消息推送(如每天中午推送订单统计),共同支撑苍穹外卖的定时、实时业务需求。