技术派项目学习笔记(三):策略模式 + WebSocket 深度解析(最高标准版)

源码口径更新(2026-09-29):本文保留学习笔记与原理示例。项目实际实现、个人增量和测试边界请先看项目总览;示例数字不代表可复核的项目实测。

技术派项目学习笔记(三):策略模式 + WebSocket 深度解析(最高标准版)

作者:Backend Architect
日期:2026-06-09
目标:对简历上的每一句话进行源码级验证,指出所有问题,给出生产级修复方案


📋 目录

  1. 简历描述验证
  2. 源码级深度解析
  3. 发现的重大问题
  4. 修复方案
  5. 面试八股文
  6. 总结

📋 简历描述验证

简历原文

为解决多态大模型接入的扩展性问题,基于策略模式与工厂模式实现 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 {
/**
* 具体AI选择(策略标识)
*/
AISourceEnum source();

/**
* 聊天(策略方法)
*/
ChatRecordsVo chat(Long user, String question);

/**
* 异步聊天(策略方法)
*/
ChatRecordsVo asyncChat(Long user, String question, Consumer<ChatRecordsVo> consumer);
}

策略模式的三个角色:

  1. 策略接口(ChatService):定义所有策略的通用方法
  2. 具体策略(ChatGPTService、WenXinYiYanService):实现策略接口
  3. 上下文(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;

// 1. 构造器注入:Spring 会自动注入所有 ChatService 实现类
public ChatServiceFactory(List<ChatService> chatServiceList) {
chatServiceMap = Maps.newHashMapWithExpectedSize(chatServiceList.size());

// 2. 注册所有策略
for (ChatService chatService : chatServiceList) {
chatServiceMap.put(chatService.source(), chatService);
}
}

// 3. 根据策略标识,获取对应的策略实例
public ChatService getChatService(AISourceEnum aiSource) {
return chatServiceMap.get(aiSource);
}
}

执行流程:

  1. 用户选择 AI 类型(ChatGPT、文心一言等)
  2. 调用 ChatServiceFactory.getChatService(aiSource)
  3. 返回对应的 ChatService 实现类
  4. 调用 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
// 这不是标准的工厂模式,而是"注册式工厂"(Registry Pattern)
@Component
public class ChatServiceFactory {
private final Map<AISourceEnum, ChatService> chatServiceMap;

// Spring 注入所有 ChatService 实现类
public ChatServiceFactory(List<ChatService> chatServiceList) {
chatServiceMap = Maps.newHashMapWithExpectedSize(chatServiceList.size());
for (ChatService chatService : chatServiceList) {
chatServiceMap.put(chatService.source(), chatService);
}
}

// 根据 AI 类型,获取对应的 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 {
/**
* 配置消息代理(Broker)
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 1. 启用简单消息代理(内存)
// 客户端订阅 /chat/rsp,服务器往 /chat/rsp 发送消息
config.enableSimpleBroker("/chat", "/msg");

// 2. 设置应用前缀
// 客户端发送消息到 /app/chat,会被 @MessageMapping("chat") 处理
config.setApplicationDestinationPrefixes("/app");
}

/**
* 注册 WebSocket 端点
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 1. 注册端点 /gpt/{id}/{aiType}
// 客户端创建 WebSocket 连接:new WebSocket("ws://localhost:8080/gpt/123/ChatGPT")
registry.addEndpoint("/gpt/{id}/{aiType}", "/notify")
.setHandshakeHandler(new AuthHandshakeHandler())
.addInterceptors(new AuthHandshakeInterceptor())
.setAllowedOriginPatterns("*");
}
}

执行流程:

  1. 客户端创建 WebSocket 连接:new WebSocket("ws://localhost:8080/gpt/123/ChatGPT")
  2. 服务器端 registerStompEndpoints() 处理连接请求
  3. 客户端订阅消息:stompClient.subscribe("/user/chat/rsp", callback)
  4. 服务器端 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) {
// 1. convertAndSendToUser() 会往 /user/{session}/chat/rsp 发送消息
// 2. 客户端订阅 /user/chat/rsp,就能收到消息
WebSocketResponseUtil.sendMsgToUser(session, "/chat/rsp", response);
}

/**
* 执行 AI 聊天,并推送结果
*/
public void sendMsgToUser(AISourceEnum ai, String session, String question) {
if (ai == null) {
// 自动选择 AI 类型
chatFacade.autoChat(question, vo -> response(session, vo)); // 回调方式推送
} else {
chatFacade.autoChat(ai, question, vo -> response(session, vo)); // 回调方式推送
}
}
}

流式推送的原理:

  1. AI 生成回答是一个流式的过程(一个字一个字生成)
  2. 每生成一个字,就调用一次 response(session, vo)
  3. 客户端收到消息后,把字追加到页面上
  4. 用户就能看到打字机效果(一个字一个字出现)

为什么是”毫秒级响应”?

  • 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) // ❗❗ 只有 4 个核心线程
.maxPoolSize(10) // ❗❗ 最多 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); // ❗❗ 如果 autoChat() 抛出异常,这里不会执行
}

修复方案:

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; // 最少间隔 1 秒

@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; // 最少间隔 1 秒

@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 可以传输结构化的消息(订阅、发送、消息)

📝 总结

本文讲了什么?

  1. 策略模式:ChatService 接口 + 多个实现类
  2. 工厂模式:ChatServiceFactory(注册式工厂)
  3. WebSocket:STOMP over WebSocket,流式推送

发现的问题

  1. WebSocket 线程池配置可能不足
  2. 没有错误处理
  3. 没有限流

修复方案

  1. 增大线程池:corePoolSize=50, maxPoolSize=200
  2. 添加错误处理:捕获异常,发送错误信息给用户
  3. 添加限流:限制用户发送消息的频率

本文完!


技术派项目学习笔记(三):策略模式 + WebSocket 深度解析(最高标准版)
https://whyalwaysme.lol/2026/06/09/2026-06-09-devlink-strategy-websocket-ultimate/
作者
Cassiur
发布于
2026年6月9日
许可协议