Spring AI Ollama 连接超时问题排查与解决:OkHttp 读超时配置全指南

Spring AI Ollama 连接本地模型超时问题完全解决指南

一、问题现象

在 Spring Boot 3.2.5 项目中使用 spring-ai-ollama-spring-boot-starter(版本 1.0.0-M6)连接本地 Ollama 部署的 qwen2.5:7b-instruct 模型时,调用聊天接口(例如 RAG 问答)会在约 10 秒后抛出以下异常:

复制代码
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:11434/api/chat": timeout
    at org.springframework.web.client.DefaultRestClient...
Caused by: java.net.SocketTimeoutException: timeout
    at okio.SocketAsyncTimeout.newTimeoutException(JvmOkio.kt:146)
    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(...)

尽管在 application.yml 中已经配置了 spring.ai.ollama.chat.options.timeout: 120s,超时仍然准时在 10 秒左右发生,导致模型生成未完成就被中断。

二、问题场景

  • 本地 Ollama 模型响应慢 :使用 7B 或更大参数量的模型(如 qwen2.5:7b-instruct),或者提问复杂度较高时,Ollama 服务端需要较长时间(可能十几秒甚至几十秒)才能返回第一个 token 或完整响应。
  • 只配置了服务端超时,未配置客户端 HTTP 超时 :开发者往往认为 spring.ai.ollama.chat.options.timeout 就足够控制整个请求的超时,但实际它只控制发送给 Ollama API 的 timeout 参数(告诉服务端最多生成多久),并不影响 Java 客户端等待响应的时长。
  • 底层 HTTP 客户端为 OkHttp :Spring AI Ollama 在无自定义配置时,默认通过 OkHttp3ClientHttpRequestFactory 使用 OkHttp 发起请求。OkHttp 的默认读超时为 10 秒,这就是超时发生在 10 秒的根本原因。

三、根因分析

1. 两层超时机制相互独立

  • 模型层超时(chat.options.timeout

    该值会被序列化到 POST /api/chat 请求体中的 options.timeout 字段,用于告知 Ollama 服务端允许的最长生成时间。服务端如果超时,会主动中断生成并返回错误。

  • HTTP 客户端层超时(OkHttp 读超时)

    这是 Java 应用等待服务器返回响应的最大时间。如果服务端处理慢(比如模型生成耗时较长),客户端会在达到读超时后直接抛出 SocketTimeoutException无论服务端是否仍在正常工作

    OkHttp 默认 readTimeout = 10_000ms(10 秒)。

只有 HTTP 读超时 > 模型生成所需时间时,请求才能正常完成。 反之,即使服务端允许生成更久,客户端也会先断开连接。

2. 常见配置为何不生效?

  • spring.restclient.read-timeout 无效

    spring.restclient 属性通过 RestClientCustomizer 全局修改 RestClient.Builder,但 Spring AI Ollama 自动配置内部是独立创建 RestClient 的,并未应用全局定制器,因此该配置无法传递到 Ollama 所用客户端。

  • SimpleClientHttpRequestFactory 无效

    实际堆栈中显示底层为 okhttp3.OkHttpClient,而非 JDK 默认的 HttpURLConnection(对应 SimpleClientHttpRequestFactory)。配置后者当然不起作用。

  • spring.okhttp.read-timeout 无效(或直接启动报错)

    Spring Boot 对 OkHttp 的属性前缀是 spring.okhttp,而非 okhttp。即使写成正确前缀,Ollama 自动配置也可能没有使用 Spring 管理的 OkHttpClient Bean,而是直接创建了一个默认 OkHttpClient,因此全局配置同样不生效。

此外,若在 YAML 中不慎写出两个顶级 spring: 键,会触发 DuplicateKeyException 导致启动失败。

4. 自定义 Bean 时的常见坑

直接创建 OllamaApi Bean 时,需注意其构造函数签名在 1.0.0-M6 版本中为:

java 复制代码
public OllamaApi(String baseUrl, 
                 RestClient.Builder restClientBuilder, 
                 WebClient.Builder webClientBuilder)

而不是 (String, RestClient)。错误地调用构造函数会导致编译失败。


四、最终解决方案

自定义 OllamaApi Bean,显式控制 OkHttp 超时

直接通过配置类覆盖 OllamaApi Bean,创建一个具有足够长读超时的 OkHttpClient,并将其通过 RestClient.Builder 注入到 OllamaApi 中。此方案完全绕过 Spring 的全局 OkHttp 配置,从根源上解决问题。

步骤:

  1. 在项目中新增配置类 OllamaTimeoutConfig.java
  2. 使用 @Value 注入 spring.ai.ollama.base-url
  3. 构建自定义超时的 OkHttpClient
  4. 创建 RestClient.Builder 并设置 OkHttp3ClientHttpRequestFactory(虽然已过时,但功能正常,可忽略警告)。
  5. 提供空 WebClient.Builder 实例。
  6. 调用正确的 OllamaApi 三参数构造器并返回 Bean。

完整代码:

java 复制代码
package com.badao.ai.config;

import okhttp3.OkHttpClient;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

import java.time.Duration;

@Configuration
public class OllamaTimeoutConfig {

    @Value("${spring.ai.ollama.base-url}")
    private String baseUrl;

    @Bean
    public OllamaApi ollamaApi() {
        // 1. 自定义 OkHttpClient 超时
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(Duration.ofSeconds(30))       // 连接超时
                .readTimeout(Duration.ofMinutes(3))           // 读超时 3 分钟,大于模型 timeout
                .writeTimeout(Duration.ofSeconds(60))         // 写超时
                .build();

        // 2. 创建 OkHttp3ClientHttpRequestFactory(已过时但可用)
        OkHttp3ClientHttpRequestFactory factory =
                new OkHttp3ClientHttpRequestFactory(okHttpClient);

        // 3. 构建 RestClient.Builder,注入自定义 factory
        RestClient.Builder restClientBuilder = RestClient.builder()
                .baseUrl(baseUrl)
                .requestFactory(factory);

        // 4. 提供 WebClient.Builder(必须,传默认空 builder 即可)
        WebClient.Builder webClientBuilder = WebClient.builder();

        // 5. 调用 OllamaApi 实际构造函数
        return new OllamaApi(baseUrl, restClientBuilder, webClientBuilder);
    }
}

YAML 配置精简:

既然已经通过代码完全掌控了 HTTP 客户端超时,就可以移除 application.yml 中的 spring.restclientspring.okhttp 等无关超时配置,保持清晰:

yaml 复制代码
server:
  port: 885

logging:
  level:
    com.badao: debug
    org.springframework.ai: debug

spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: qwen2.5:7b-instruct
          temperature: 0.5
          timeout: 120s          # 服务端模型生成超时,依然建议保留
      embedding:
        options:
          model: nomic-embed-text
          timeout: 120s
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

关键要点

  • 读超时必须大于模型超时 :这里 readTimeout = 3 分钟,而 chat.options.timeout = 2 分钟,留有充足缓冲。
  • OkHttp3ClientHttpRequestFactory 过时警告:不影响功能,可忽略。如需消除,需整体切换到其他 HTTP 客户端(如 JDK HttpClient),但会增加配置复杂度,不值得。
  • 不要添加额外的 YAML OkHttp 配置,避免干扰。

五、验证效果

  1. 重新编译并启动应用。
  2. 发送之前会导致超时的 RAG 请求。
  3. 观察日志,不再出现 Read timed outSocketTimeoutException
  4. 模型正常返回生成结果,即使耗时超过 10 秒、甚至 1 分钟,也能顺利完成。

六、总结

本次问题的本质是 Spring AI Ollama 使用的底层 OkHttp 读超时默认过短 ,且 YAML 配置中的服务端超时选项无法控制客户端行为,加上 Spring Boot 全局 OkHttp 属性与 Ollama 自动配置并不互通,导致常规配置尝试全部失效。

最终通过自定义 OllamaApi Bean 直接构建带超时的 OkHttpClient,并依其正确的构造函数注入,彻底解决了超时问题。该方案稳定可靠,推荐遇到同类问题的开发者采用。

相关推荐
道友可好2 小时前
Spec Kit:GitHub 官方出品,规范即代码
前端·人工智能·后端
weixin_505154463 小时前
打通工业安全治理“最后一公分”:Bowell 发布 Runtime 治理平台
大数据·人工智能·安全·3d·数字孪生·数据可视化
烬、、、3 小时前
如何用 Claude Code 调用 gpt-image2 生成图片?
人工智能·笔记·gpt·prompt·skills
郑州光合科技余经理3 小时前
海外版外卖系统:如何快速搭建国际化外卖平台
java·开发语言·前端·人工智能·小程序·系统架构·php
王哈哈^_^3 小时前
YOLO分类任务训练教程:从数据准备到模型部署全流程
人工智能·yolo·计算机视觉·分类·数据挖掘
下午写HelloWorld3 小时前
同态加密(Homomorphic Encryption, HE)
人工智能·算法·密码学·同态加密
尚可签3 小时前
小烟改写工具:让文字表达更自然,让文档改写更高效
人工智能
小何code3 小时前
【Python零基础入门】第10篇:Python列表方法与应用实例
数据库·人工智能·python
聚名网3 小时前
中文域名深化实体应用,稳步对接智能交互场景
人工智能·经验分享