源码口径更新(2026-09-29):本文保留学习笔记与原理示例。项目实际实现、个人增量和测试边界请先看项目总览;示例数字不代表可复核的项目实测。
技术派项目学习笔记(三):策略模式 + WebSocket 深度解析(最高标准版)
作者:Backend Architect
日期:2026-06-09
目标:对简历上的每一句话进行源码级验证,指出所有问题,给出生产级修复方案
📋 目录
- 简历描述验证
- 源码级深度解析
- 发现的重大问题
- 修复方案
- 面试八股文
- 总结
📋 简历描述验证
简历原文
为解决多态大模型接入的扩展性问题,基于策略模式与工厂模式实现 LLM 调用链路的统一抽象与热插拔;后端结合 WebSocket 推送 Stream 流,实现前端毫秒级响应体验
验证结果
✅ 基本准确,但有以下细节需要补充:
| 技术点 |
简历描述 |
实际实现 |
是否准确 |
| 策略模式 |
“基于策略模式” |
ChatService 接口 + 多个实现类 |
✅ 准确 |
| 工厂模式 |
“工厂模式” |
ChatServiceFactory(注册式工厂) |
✅ 准确 |
| 热插拔 |
“热插拔” |
添加新的 ChatService 实现类即可 |
✅ 准确 |
| WebSocket |
“WebSocket 推送 Stream 流” |
使用 STOMP over WebSocket |
✅ 准确 |
| 毫秒级响应 |
“毫秒级响应体验” |
使用 SimpMessagingTemplate 推送 |
✅ 准确 |
📚 源码级深度解析
1. 策略模式深度解析
1.1 策略模式是什么?
生活化比喻:
- 就像你去餐厅点餐,可以选中餐、西餐、日料
- 每种菜系都是一个策略,你可以随时切换
- 但点餐的流程是一样的(看菜单 → 点菜 → 付款 → 上菜)
源码解析:ChatService 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public interface ChatService {
AISourceEnum source();
ChatRecordsVo chat(Long user, String question);
ChatRecordsVo asyncChat(Long user, String question, Consumer<ChatRecordsVo> consumer); }
|
策略模式的三个角色:
- 策略接口(
ChatService):定义所有策略的通用方法
- 具体策略(
ChatGPTService、WenXinYiYanService):实现策略接口
- 上下文(
ChatServiceFactory):维护一个策略实例的引用
1.2 策略模式的实现
生活化比喻:
- 就像餐厅的菜单(策略接口),上面有各种菜系(具体策略)
- 你点餐后,服务员(上下文)把你的订单发给对应的厨师(具体策略)
源码解析:ChatServiceFactory(上下文)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Component public class ChatServiceFactory { private final Map<AISourceEnum, ChatService> chatServiceMap; public ChatServiceFactory(List<ChatService> chatServiceList) { chatServiceMap = Maps.newHashMapWithExpectedSize(chatServiceList.size()); for (ChatService chatService : chatServiceList) { chatServiceMap.put(chatService.source(), chatService); } } public ChatService getChatService(AISourceEnum aiSource) { return chatServiceMap.get(aiSource); } }
|
执行流程:
- 用户选择 AI 类型(ChatGPT、文心一言等)
- 调用
ChatServiceFactory.getChatService(aiSource)
- 返回对应的
ChatService 实现类
- 调用
chatService.chat(user, question)
优点:
- ✅ 开闭原则:新增 AI 类型,只需要添加新的
ChatService 实现类,不用修改现有代码
- ✅ 避免大量的 if-else:不用写
if (aiType == CHAT_GPT) { ... } else if (aiType == WEN_XIN_YI_YAN) { ... }
1.3 工厂模式深度解析
生活化比喻:
- 就像工厂生产产品,你只需要告诉工厂”我要 ChatGPT”,工厂就会给你一个 ChatGPT 实例
- 你不需要知道 ChatGPT 是怎么创建的,只需要知道怎么用
源码解析:ChatServiceFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Component public class ChatServiceFactory { private final Map<AISourceEnum, ChatService> chatServiceMap; public ChatServiceFactory(List<ChatService> chatServiceList) { chatServiceMap = Maps.newHashMapWithExpectedSize(chatServiceList.size()); for (ChatService chatService : chatServiceList) { chatServiceMap.put(chatService.source(), chatService); } } public ChatService getChatService(AISourceEnum aiSource) { return chatServiceMap.get(aiSource); } }
|
标准工厂模式 vs 注册式工厂:
| 特性 |
标准工厂模式 |
注册式工厂(当前实现) |
| 创建实例 |
工厂创建 |
Spring 创建 |
| 依赖注入 |
不支持 |
支持 |
| 性能 |
差(每次创建新实例) |
好(复用实例) |
| 复杂度 |
低 |
中 |
为什么用注册式工厂?
- ✅ 支持依赖注入:所有的
ChatService 实现类都是 Spring Bean,可以注入其他 Bean
- ✅ 性能好:复用实例,不每次创建新实例
- ✅ 代码简洁:不需要写大量的 if-else
2. WebSocket 深度解析
2.1 WebSocket 是什么?
生活化比喻:
- HTTP:就像你给朋友写信,每次都要重新写地址、贴邮票、寄出去
- WebSocket:就像你给朋友打电话,建立连接后,可以随时互相说话
HTTP vs WebSocket:
| 特性 |
HTTP |
WebSocket |
| 连接 |
短连接(每次请求都要建立连接) |
长连接(建立一次,一直保持) |
| 方向 |
单向(客户端请求,服务器响应) |
双向(客户端和服务器都可以主动发送消息) |
| 实时性 |
差(要轮询) |
好(服务器可以主动推送) |
2.2 STOMP over WebSocket
生活化比喻:
- WebSocket:就像一条电话线,可以传输任何数据
- STOMP:就像电话线里的语言(比如中文、英文),规定了数据的格式
STOMP 协议:
- 订阅:
SUBSCRIBE /user/chat/rsp
- 发送:
SEND /app/chat
- 消息:
MESSAGE /chat/rsp
源码解析:WsChatConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Configuration @EnableWebSocketMessageBroker public class WsChatConfig implements WebSocketMessageBrokerConfigurer {
@Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/chat", "/msg"); config.setApplicationDestinationPrefixes("/app"); }
@Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gpt/{id}/{aiType}", "/notify") .setHandshakeHandler(new AuthHandshakeHandler()) .addInterceptors(new AuthHandshakeInterceptor()) .setAllowedOriginPatterns("*"); } }
|
执行流程:
- 客户端创建 WebSocket 连接:
new WebSocket("ws://localhost:8080/gpt/123/ChatGPT")
- 服务器端
registerStompEndpoints() 处理连接请求
- 客户端订阅消息:
stompClient.subscribe("/user/chat/rsp", callback)
- 服务器端
WsAnswerHelper.response() 发送消息:simpMessagingTemplate.convertAndSendToUser(session, "/chat/rsp", response)
2.3 WebSocket 流式推送
生活化比喻:
- 就像你看直播,主播说一句话,你马上就能听到
- 而不是等主播说完所有话,再一次性发给你
源码解析:WsAnswerHelper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Component public class WsAnswerHelper {
public void response(String session, ChatRecordsVo response) { WebSocketResponseUtil.sendMsgToUser(session, "/chat/rsp", response); }
public void sendMsgToUser(AISourceEnum ai, String session, String question) { if (ai == null) { chatFacade.autoChat(question, vo -> response(session, vo)); } else { chatFacade.autoChat(ai, question, vo -> response(session, vo)); } } }
|
流式推送的原理:
- AI 生成回答是一个流式的过程(一个字一个字生成)
- 每生成一个字,就调用一次
response(session, vo)
- 客户端收到消息后,把字追加到页面上
- 用户就能看到打字机效果(一个字一个字出现)
为什么是”毫秒级响应”?
- HTTP 轮询:客户端每隔 1 秒发一次请求,检查 AI 是否回答完成
- WebSocket 推送:AI 生成一个字,就推送一次,延迟只有几毫秒
🚨 发现的重大问题
🟡 中等问题(性能问题、设计缺陷)
问题 1:WebSocket 线程池配置可能不足
问题描述:
- 在
WsChatConfig.configureClientInboundChannel() 中,线程池配置为 corePoolSize=4, maxPoolSize=10
- 如果有 1000 个用户同时聊天,只有 10 个线程处理
问题代码:
1 2 3 4 5 6 7
| @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.taskExecutor() .corePoolSize(4) .maxPoolSize(10) .keepAliveSeconds(60); }
|
修复方案:
1 2 3 4 5 6 7 8
| @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.taskExecutor() .corePoolSize(50) .maxPoolSize(200) .queueCapacity(1000) .keepAliveSeconds(60); }
|
问题 2:没有错误处理
问题描述:
- 在
WsAnswerHelper.sendMsgToUser() 中,如果 chatFacade.autoChat() 抛出异常,没有捕获
问题代码:
1 2 3 4
| private void sendMsgToUser(String session, String question) { ChatRecordsVo res = chatFacade.autoChat(question, vo -> response(session, vo)); log.info("AI直接返回:{}", res); }
|
修复方案:
1 2 3 4 5 6 7 8 9 10 11 12 13
| private void sendMsgToUser(String session, String question) { try { ChatRecordsVo res = chatFacade.autoChat(question, vo -> response(session, vo)); log.info("AI直接返回:{}", res); } catch (Exception e) { log.error("AI聊天失败,session: {}, question: {}", session, question, e); ChatRecordsVo errorResponse = new ChatRecordsVo(); errorResponse.setError("AI服务暂时不可用,请稍后再试"); response(session, errorResponse); } }
|
🟢 轻微问题(代码规范、可维护性)
问题 3:没有限流
问题描述:
- 没有限制用户发送消息的频率
- 恶意用户可以向 AI 发送大量请求,导致资源耗尽
修复方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Component public class RateLimitInterceptor implements ChannelInterceptor { private final Map<String, Long> lastMessageTime = new ConcurrentHashMap<>(); private static final long MIN_INTERVAL = 1000; @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { String session = (String) message.getHeaders().get("session"); long now = System.currentTimeMillis(); Long lastTime = lastMessageTime.get(session); if (lastTime != null && (now - lastTime) < MIN_INTERVAL) { throw new IllegalArgumentException("发送消息太频繁,请稍后再试"); } lastMessageTime.put(session, now); return message; } }
|
🔧 修复方案
修复方案 1:增大 WebSocket 线程池
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Configuration @EnableWebSocketMessageBroker public class WsChatConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.taskExecutor() .corePoolSize(50) .maxPoolSize(200) .queueCapacity(1000) .keepAliveSeconds(60); registration.interceptors(channelInInterceptor()); } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { registration.taskExecutor() .corePoolSize(50) .maxPoolSize(200) .queueCapacity(1000) .keepAliveSeconds(60); registration.interceptors(channelOutInterceptor()); } }
|
修复方案 2:添加错误处理
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Component public class WsAnswerHelper { public void sendMsgToUser(AISourceEnum ai, String session, String question) { try { if (ai == null) { chatFacade.autoChat(question, vo -> response(session, vo)); } else { chatFacade.autoChat(ai, question, vo -> response(session, vo)); } } catch (Exception e) { log.error("AI聊天失败,session: {}, question: {}", session, question, e); ChatRecordsVo errorResponse = new ChatRecordsVo(); errorResponse.setError("AI服务暂时不可用,请稍后再试"); response(session, errorResponse); } } }
|
修复方案 3:添加限流
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Component public class RateLimitInterceptor implements ChannelInterceptor { private final Map<String, Long> lastMessageTime = new ConcurrentHashMap<>(); private static final long MIN_INTERVAL = 1000; @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { String session = (String) message.getHeaders().get("session"); long now = System.currentTimeMillis(); Long lastTime = lastMessageTime.get(session); if (lastTime != null && (now - lastTime) < MIN_INTERVAL) { throw new IllegalArgumentException("发送消息太频繁,请稍后再试"); } lastMessageTime.put(session, now); return message; } }
|
📚 面试八股文
策略模式
问题 1:什么是策略模式?有什么优点?
回答:
- 策略模式:定义一系列策略,把它们封装起来,并且使它们可以互相替换
- 优点:开闭原则、避免大量的 if-else
问题 2:策略模式和工厂模式有什么区别?
回答:
- 策略模式:关注算法的替换
- 工厂模式:关注对象的创建
WebSocket
问题 3:什么是 WebSocket?和 HTTP 有什么区别?
回答:
- WebSocket:双向通信协议,客户端和服务器都可以主动发送消息
- HTTP:单向通信协议,只能客户端请求,服务器响应
问题 4:什么是 STOMP?有什么作用?
回答:
- STOMP:简单的消息协议,规定了消息的格式
- 作用:让 WebSocket 可以传输结构化的消息(订阅、发送、消息)
📝 总结
本文讲了什么?
- 策略模式:
ChatService 接口 + 多个实现类
- 工厂模式:
ChatServiceFactory(注册式工厂)
- WebSocket:STOMP over WebSocket,流式推送
发现的问题
- WebSocket 线程池配置可能不足
- 没有错误处理
- 没有限流
修复方案
- 增大线程池:
corePoolSize=50, maxPoolSize=200
- 添加错误处理:捕获异常,发送错误信息给用户
- 添加限流:限制用户发送消息的频率
本文完!