先说结论:Spring Boot 3 接 Ollama 做本地大模型推理,接口 5 秒以上不是新鲜事,但问题大多不在“推理太慢”,而在于你用错了调用姿势。
我在这条路上折腾了不少时间,最开始的思路特别直接——把用户问题发给 Ollama,同步等着它把结果全部生成完再返回给前端。模型用的是 7B 量化版,一次性回复 200 多个 token 很正常,结果接口普遍 5 秒到 8 秒。后来把架构改成“流式输出 + 首 token 计时”,同一个模型同一块显卡,用户能感知的等待时间压到了 300ms 左右,接口层面的首 token 指标也稳稳落进 500ms 以内。
这篇文章我把自己踩过的坑、改过的代码、调过的参数全部整理出来。不管你是个人项目还是企业级服务,只要用 Spring Boot 调 Ollama,这套优化思路都适用。
1. 响应慢的根源:先搞清楚这 5 秒到底花在哪
动手优化之前,得先把延迟拆开看。Ollama 本地推理的一次完整请求,从 Java 服务发起到最后拿到完整结果,时间主要消耗在这几个环节上。
1.1 推理本身没你想的那么慢
很多人一看到 5 秒就觉得是显卡不行、模型太大。实际上模型推理的“计算时间”在延迟占比里往往不是大头。我实测过几次典型场景,用 4060 Ti 16G 跑 Qwen2.5 7B Instruct 的 Q4_K_M 量化版,一个 20 token 的 prompt,首 token 生成时间在 200ms 左右,后续每个 token 大约 20ms 到 30ms。也就是说,生成 50 个 token 的完整回答,纯推理时间大概 1.2 到 1.7 秒。
这个速度对一个本地模型来说已经不错了,但为什么接口会跑到 5 秒以上?
因为你的请求里还有三个隐性开销:
- 模型加载时间:模型从磁盘读入显存,7B 模型 Q4 量化后约 4.5GB,冷启动加载普遍需要 2 到 5 秒。
- 预填充阶段:模型要先把你的 prompt 完整“读一遍”,这个耗时跟输入 token 数成正比,输入越长越慢。
- 同步等待完整输出:如果代码里用的是同步调用,必须等模型把最后一个 token 生成完毕才能返回,总时间等于“首 token 时间 + 所有 token 的生成时间”。
这三个开销叠加,接口 5 秒以上完全正常。
1.2 500ms 这个目标,定义得不对就永远做不到
优化之前必须搞清楚一个概念:500ms 以内到底指什么?
如果你的需求是“接口同步返回完整模型结果”,在消费级显卡上跑 7B 模型,老实说很难稳定做到 500ms 以内。因为只生成 50 个 token 也需要 1 秒以上,这还不算预填充和网络开销。
但对用户来说,“响应快”的真实感知是:发出问题后多久能看到第一个字或第一个词。这个指标在行业内叫 TTFT,Time To First Token,也就是首 token 延迟。
Ollama 的流式接口天然支持这个需求,模型每生成一个 token 就会通过 SSE 或 JSON 流推给客户端。Java 服务拿到第一个 token 就可以立刻转发给前端,用户几乎瞬间看到内容开始滚动。这时候接口层计时,首 token 进 500ms 是完全可以做到的。
所以整个优化方向就一句话:指标切到首 token 延迟,响应方式切到流式,同时想办法把预填充跟模型加载的时间压小。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一步优化:换用流式响应,吃掉最大的延迟块
思路确定了,代码就要跟着改。我之前用的是 JDK 自带 HttpClient 发同步请求,等响应体全部返回再处理,这显然不行。现在选项有两个:Spring WebFlux 的 WebClient,或者 JDK 11+ 的 HttpClient 配合响应式行处理。
2.1 流式方案怎么选:WebClient 还是 JDK HttpClient
如果是全新项目,或者你的 Spring Boot 服务已经引入了 spring-boot-starter-webflux,那我建议直接用 WebClient。它天生支持流式,配合 Flux 做后续的数据处理很方便。
但很多 Spring Boot 项目用的是 spring-boot-starter-web,也就是传统 Servlet 栈。引入 WebFlux 依赖并不冲突,WebClient 还是可以正常使用,只是 Spring Boot 会优先采用 MVC 作为 Web 框架。这一点实测下来没毛病。
如果不想引入 WebFlux 依赖,JDK 自带的 HttpClient 也够用。它有一个 BodyHandlers.ofLines(),能把响应体按行返回,Ollama 的流式输出本来就是一行一个 JSON 对象,用这个处理非常顺手。
两个方案的取舍看你的依赖情况:项目里已有 WebFlux 就用 WebClient,不想引入新依赖就用 JDK HttpClient。我给两个代码示例。
2.2 Spring Boot 3 接 Ollama 的完整流式代码(WebClient 版)
先加依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
然后配置 WebClient 连接 Ollama:
java复制@Configuration
public class OllamaConfig {
@Bean
public WebClient ollamaWebClient() {
