Spring Alibaba AI Graph流式处理超时问题排查与解决方案

文章概要 在使用 Spring AI Alibaba Graph Core 框架开发流式处理节点时,遇到了一个典型的响应式编程陷阱:在 WebFlux 的 Reactor 线程中执行阻塞操作导致的超时问题。本文详细记录了从问题发现、逐步排查、根因分析到最终解决的完整过程,并总结了响应式编程的最佳实践。 关键词: Spring AI, WebFlux, Reactor, 流式处理, 响应式编程, 阻塞调用 一、问题现象 1.1 初始症状 在 05-observability-langfuse 模块中实现了一个 StreamingChatNode 用于处理 LLM 的流式响应,但在运行时出现以下问题: 流式处理超时:节点执行后长时间无响应,最终超时 日志异常:看到流被订阅,但没有后续的数据发射日志 程序卡住:整个图的执行流程在 streaming 节点后停滞 1.2 关键日志 初期日志(看似正常): 1 2 3 2025-10-26T17:54:53.020 INFO - StreamingNode using ChatClient: DefaultChatClient 2025-10-26T17:54:53.056 INFO - StreamingNode chatResponseFlux subscribed 2025-10-26T17:54:53.129 INFO - StreamingNode streaming processing setup completed 后期日志(发现数据发射): 1 2 3 2025-10-26T18:02:37.823 INFO - StreamingNode chatResponseFlux emit: ChatResponse [...] textContent=推动产业结构的升级 promptTokens=820, completionTokens=684, totalTokens=1504 最终错误: ...

2025-10-26 · 13 分钟 · wx

CLAUDE使用技巧

在 AI 编程工具百花齐放的今天,Anthropic 推出的 Claude Code 正在成为开发者圈的新宠。 它不仅仅是一个代码补全工具,更像是一个能理解你项目全局、直接在终端帮你“干活”的智能编程助手。 今天,我们就来聊聊: Claude Code 有什么用? 如何安装和使用? 高效使用的技巧与最佳实践 一、Claude Code 是什么? Claude Code 是 Anthropic 推出的 终端级 AI 编程代理,能够: 理解整个代码库(不仅仅是当前文件) 执行跨文件的重构、调试 帮你写 PR、运行测试、修复 bug 与 Git、CI/CD、数据库、API 等工具链深度协作 用自然语言交互,让 AI 成为你代码仓库的“伙伴” 与 GitHub Copilot、Codeium 这类 IDE 内补全工具不同,Claude Code 的定位更高—— 它不是给你几行代码提示,而是能真正完成一个开发任务,甚至包含执行命令、提交代码、推送 PR 等全过程。 二、安装Claude Code 安装 Node.js(版本 ≥ 18.0) Ubuntu / Debian 1 2 3 curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo bash - sudo apt-get install -y nodejs node --version macOS 1 2 3 4 5 6 7 8 9 # 安装 Xcode CLI 工具 sudo xcode-select --install # 安装 Homebrew(如果已安装可跳过) /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # 安装 Node.js brew install node node --version 安装 Claude Code 1 2 npm install -g @anthropic-ai/claude-code claude --version 如果遇到 npm 权限问题,可以考虑加 sudo 或使用 nvm 管理 Node.js。 ...

2025-09-27 · 7 分钟 · wx

Git 使用技巧与最佳实践

概述 Git 是一个强大的分布式版本控制系统,适用于代码管理和团队协作。虽然现代 IDE 工具都集成了常用的 Git 功能,但掌握命令行操作仍然是开发者必备技能。本文总结了日常开发中最常用的 Git 命令和最佳实践。 Git 工作流程 Git 的工作流程围绕四个主要区域展开: 工作区(Working Directory) - 进行代码编辑的地方,可以添加新文件、修改现有文件或删除文件 暂存区(Staging Area/Index) - 临时存储区域,用于存放下次提交的快照。执行 git add 命令时,将工作区中的更改添加到暂存区 本地仓库(Local Repository) - 项目的历史记录,包含了所有已提交的更改。执行 git commit 命令时,Git 会将暂存区中的内容创建一个新的提交 远程仓库(Remote Repository) - 托管在互联网上或网络中的仓库,通常用于团队协作 基础配置 提示:命令中加入 --global 就是全局操作,否则需要在对应项目目录下执行 配置用户信息 全局配置使用者信息,用于提交工作时,展示个人信息 1 2 3 # 全局配置使用者信息,用于提交工作时展示个人信息 git config --global user.name "ideal" git config --global user.email "example@ideal.com" 初始化仓库 1 git init 克隆远程仓库 克隆分为两种方式:HTTPS 和 SSH 克隆,主要区别如下: 克隆方式 认证方式 适用场景 优势 劣势 HTTPS 用户名 + 密码 临时访问或不想配置 SSH 密钥 易用,无需额外配置 SSH 密钥 每次操作可能需要输入凭据 SSH SSH 密钥认证 长期开发和高安全性需求 免密码推拉代码,安全性更高 需要配置 SSH 密钥 1 2 3 4 5 6 7 8 # 生成 SSH 密钥 ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # HTTPS 方式克隆 git clone https://github.com/wx-coding/git-test.git # SSH 方式克隆 git clone git@github.com:wx-coding/git-test.git 日常操作 查看仓库状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 wx@wxdeMacBook-Pro git-test % git status On branch main Your branch is up to date with 'origin/main'. Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: a.txt Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: .gitignore modified: a.txt 添加文件到暂存区 暂存区是一个临时的区域,用于保存你对文件的更改,可以将它理解为一个“准备提交”的区域 ...

2025-09-05 · 6 分钟 · wx

Java面试题——Bean生命周期

Bean 生命周期 Bean的创建过程步骤比较多,这里结合代码一起汇总一下 通过 BeanDefinition 获取 bean 的定义信息 调用构造函数实例化 bean Bean 的依赖注入 处理 Aware 接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware) Bean 的前置处理器 BeanPostProcessor - 前置(postProcessBeforeInitialization) 初始化方法(InitializingBean、init-method) Bean 的后置处理器 BeanPostProcessor - 后置(postProcessAfterInitialization) 销毁 bean(DisposableBean、destroy-method) 牢记执行流程,流程如下 结合代码查看Bean的创建过程 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 // User.java Bean 创建 @Component public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean { private String name; public User(){ System.out.println("1. 调用了构造函数"); } @Value("test") public void setName(String name){ this.name = name; System.out.println("2. 依赖注入"); } @Override public void setBeanName(String name) { System.out.println("3. setBeanName 执行了 " + name); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("4. setBeanFactory 执行了 " + beanFactory.getClass().getName()); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("5. setApplicationContext 执行了 " + applicationContext.getApplicationName()); } @PostConstruct public void init(){ System.out.println("7. init() 方法执行了"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("8. afterPropertiesSet() 方法执行了"); } @PreDestroy public void destroy(){ System.out.println("10. destroy() 销毁方法执行了"); } } 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 31 32 33 // MyBeanPostProcessor.java bean 后置处理器 @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof User) { User user = (User) bean; System.out.println("6. postProcessBeforeInitialization user = " + user); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("user")) { System.out.println("9. postProcessAfterInitialization user -> 对象方法开始增强"); // // cglib 代理对象 // Enhancer enhancer = new Enhancer(); // // 设置需要增强的类 // enhancer.setSuperclass(bean.getClass()); // // 执行会调方法,增强方法 // enhancer.setCallback(new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // // 执行目标方法 // return method.invoke(bean, args); // } // }); // return enhancer.create(); } return bean; } } 1 2 3 4 5 6 7 8 9 // Main.java 启动类 @SpringBootApplication public class Main { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(Main.class, args); User bean = applicationContext.getBean(User.class); System.out.println(bean); } } 启动程序,执行结果如下 ...

2025-08-18 · 2 分钟

第一次提交

Introduction 记录一下,初始化~~

2025-08-17 · 1 分钟