十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring AI Advisor局部作用域与Redis会话管理实践

Spring AI Advisor局部作用域与Redis会话管理实践 1. Spring AI Advisor局部作用域解析在Spring AI框架中Advisor的设计遵循了AOP面向切面编程的思想但它的作用范围可以通过配置实现精细化控制。从示例中可以看到timerAdvisor仅在call2方法中生效而在call1中不起作用这种局部作用域的实现主要基于以下几个机制Advisor的Order属性每个Advisor通过getOrder()方法返回的数值决定了其执行优先级。数值越小优先级越高当多个Advisor存在时这个属性会影响拦截链的构建顺序。条件匹配规则Spring AI内部使用Pointcut切点机制来匹配目标方法。可以通过自定义Pointcut实现精确的方法匹配例如Bean public Advisor timerAdvisor() { AspectJExpressionPointcut pointcut new AspectJExpressionPointcut(); pointcut.setExpression(execution(* com.example.Service.call2(..))); return new DefaultPointcutAdvisor(pointcut, new TimerInterceptor()); }上下文参数传递如示例所示通过chatClientRequest.context()可以获取调用上下文参数这些参数可以动态控制Advisor是否生效。实际开发中常见的坑如果发现Advisor未按预期生效首先检查getOrder()返回值是否被其他Advisor覆盖其次验证Pointcut表达式是否能正确匹配目标方法。2. 全局参数传递机制详解2.1 上下文参数传递实现示例中的ThirdAdvisor展示了如何获取全局传递的参数Object a chatClientRequest.context().get(A);这种参数传递机制的核心实现原理包括Context参数存储结构底层使用ThreadLocal维护的Map结构保证请求级别的参数隔离参数生命周期从ChatClient构建开始贯穿整个请求处理链类型安全建议推荐使用类型明确的参数存取方式chatClientRequest.context().put(timeout, Duration.ofSeconds(30)); Duration timeout chatClientRequest.context().get(timeout, Duration.class);2.2 参数传递的最佳实践命名规范采用全大写下划线命名如USER_ID避免键名冲突性能考量复杂对象建议先序列化为JSON字符串安全注意敏感参数应当加密存储例如String encrypted AESUtil.encrypt(paramValue); chatClientRequest.context().put(secure_param, encrypted);3. 基于Redis的多轮会话完整实现3.1 技术架构设计相比内存和JDBC方案Redis实现的优势在于分布式会话支持TTL自动过期高性能读写持久化保障整体架构流程sequenceDiagram participant Client participant Controller participant Advisor participant Redis participant AI Model Client-Controller: 提问会话ID Controller-Advisor: 封装请求 Advisor-Redis: 获取历史消息 Redis--Advisor: 返回消息列表 Advisor-AI Model: 提交增强后的Prompt AI Model--Advisor: 返回响应 Advisor-Redis: 存储新消息 Advisor--Controller: 返回流式响应 Controller--Client: 逐步输出3.2 核心组件实现细节3.2.1 可序列化消息包装Message接口的序列化问题需要特别注意public class SerializableMessage implements Serializable { // 必须显式声明serialVersionUID private static final long serialVersionUID 1L; // 处理MessageType的序列化特殊情况 private String messageType; public SerializableMessage(Message message) { this.messageType message.getMessageType().name(); // ...其他字段 } public Message getMessage() { return switch(MessageType.valueOf(messageType)) { case USER - new UserMessage(content); case ASSISTANT - new AssistantMessage(content); // ...其他类型处理 }; } }3.2.2 Redis内存管理策略Key设计规范使用业务前缀如AI_CHAT:包含会话ID和用户ID防越权示例AI_CHAT:{userId}:{sessionId}过期策略配置spring: data: redis: # 默认30分钟过期 timeout: 1800内存优化技巧使用Hash结构存储消息压缩长文本内容限制历史消息条数如最近20条3.3 完整工作流程示例初始化请求// 前端传递会话ID String chatId UUID.randomUUID().toString(); // 首次请求建立会话 FluxString response chatClient.prompt() .advisors(advisor - advisor.param(id, chatId)) .user(如何学习Spring AI?) .stream() .content();续聊实现// 使用相同chatId继续对话 FluxString followup chatClient.prompt() .advisors(advisor - advisor.param(id, existingChatId)) .user(还有更详细的学习路径吗?) .stream() .content();会话清除DeleteMapping(/session/{chatId}) public void clearSession(PathVariable String chatId) { redisTemplate.delete(REDIS_MESSAGE_KEY chatId); }4. 生产环境注意事项4.1 性能优化方案Pipeline批量操作ListObject results redisTemplate.executePipelined((RedisCallbackObject) connection - { for (Message message : messages) { connection.stringCommands().set( (MSG_ chatId).getBytes(), serialize(message) ); } return null; });二级缓存策略Cacheable(value chatMessages, key #chatId) public ListMessage getMessages(String chatId) { // Redis查询实现 }4.2 异常处理机制Redis故障降级try { return redisChatMemory.get(chatId); } catch (RedisConnectionFailureException e) { log.warn(Redis unavailable, falling back to local cache); return localCache.get(chatId); }消息压缩容错public String compressMessage(Message message) { try { return Snappy.compress(message.getText()); } catch (IOException e) { // 压缩失败返回原文 return message.getText(); } }4.3 安全防护措施会话隔离// 在获取会话前验证权限 if (!securityService.canAccessChat(userId, chatId)) { throw new AccessDeniedException(); }敏感词过滤public Message filterSensitive(Message message) { String filtered sensitiveWordFilter.filter(message.getText()); return new UserMessage(filtered, message.getMetadata()); }5. 扩展应用场景5.1 多模态会话支持扩展SerializableMessage支持图片消息public class SerializableMessage { private MessageType type; private String textContent; private byte[] imageData; // 新增多媒体类型处理 public Message getMessage() { if (type MessageType.IMAGE) { return new ImageMessage(imageData, metadata); } // ...原有逻辑 } }5.2 会话分析功能基于Redis的会话分析实现public ChatStatistics analyzeChat(String chatId) { String json redisTemplate.opsForValue().get(prefix chatId); ListSerializableMessage messages parseJson(json); return new ChatStatistics( messages.stream().filter(m - m.getType() USER).count(), messages.stream().mapToInt(m - m.getText().length()).average(), // ...其他统计指标 ); }5.3 分布式部署方案跨中心同步配置Bean public RedisTemplateString, SerializableMessage redisTemplate( RedisConnectionFactory connectionFactory) { RedisTemplateString, SerializableMessage template new RedisTemplate(); template.setConnectionFactory(connectionFactory); template.setDefaultSerializer(new Jackson2JsonRedisSerializer(SerializableMessage.class)); // 启用跨数据中心复制 template.setEnableTransactionSupport(true); return template; }通过这套基于Redis的Advisor实现我们不仅解决了官方ChatMemory的局限性还获得了分布式环境下的会话支持能力。在实际项目中建议根据业务需求适当调整序列化策略和存储结构比如对于高频访问的会话可以考虑引入本地缓存作为Redis的补充。
返回列表