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 客户端为 OkHttpSpring AI Ollama 在无自定义配置时默认通过OkHttp3ClientHttpRequestFactory使用 OkHttp 发起请求。OkHttp 的默认读超时为10 秒这就是超时发生在 10 秒的根本原因。三、根因分析1. 两层超时机制相互独立模型层超时chat.options.timeout该值会被序列化到POST /api/chat请求体中的options.timeout字段用于告知 Ollama 服务端允许的最长生成时间。服务端如果超时会主动中断生成并返回错误。HTTP 客户端层超时OkHttp 读超时这是 Java 应用等待服务器返回响应的最大时间。如果服务端处理慢比如模型生成耗时较长客户端会在达到读超时后直接抛出SocketTimeoutException无论服务端是否仍在正常工作。OkHttp 默认readTimeout 10_000ms10 秒。只有 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 管理的OkHttpClientBean而是直接创建了一个默认OkHttpClient因此全局配置同样不生效。此外若在 YAML 中不慎写出两个顶级spring:键会触发DuplicateKeyException导致启动失败。4. 自定义 Bean 时的常见坑直接创建OllamaApiBean 时需注意其构造函数签名在 1.0.0-M6 版本中为publicOllamaApi(StringbaseUrl,RestClient.BuilderrestClientBuilder,WebClient.BuilderwebClientBuilder)而不是(String, RestClient)。错误地调用构造函数会导致编译失败。四、最终解决方案自定义OllamaApiBean显式控制 OkHttp 超时直接通过配置类覆盖OllamaApiBean创建一个具有足够长读超时的OkHttpClient并将其通过RestClient.Builder注入到OllamaApi中。此方案完全绕过 Spring 的全局 OkHttp 配置从根源上解决问题。步骤在项目中新增配置类OllamaTimeoutConfig.java。使用Value注入spring.ai.ollama.base-url。构建自定义超时的OkHttpClient。创建RestClient.Builder并设置OkHttp3ClientHttpRequestFactory虽然已过时但功能正常可忽略警告。提供空WebClient.Builder实例。调用正确的OllamaApi三参数构造器并返回 Bean。完整代码packagecom.badao.ai.config;importokhttp3.OkHttpClient;importorg.springframework.ai.ollama.api.OllamaApi;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.client.OkHttp3ClientHttpRequestFactory;importorg.springframework.web.client.RestClient;importorg.springframework.web.reactive.function.client.WebClient;importjava.time.Duration;ConfigurationpublicclassOllamaTimeoutConfig{Value(${spring.ai.ollama.base-url})privateStringbaseUrl;BeanpublicOllamaApiollamaApi(){// 1. 自定义 OkHttpClient 超时OkHttpClientokHttpClientnewOkHttpClient.Builder().connectTimeout(Duration.ofSeconds(30))// 连接超时.readTimeout(Duration.ofMinutes(3))// 读超时 3 分钟大于模型 timeout.writeTimeout(Duration.ofSeconds(60))// 写超时.build();// 2. 创建 OkHttp3ClientHttpRequestFactory已过时但可用OkHttp3ClientHttpRequestFactoryfactorynewOkHttp3ClientHttpRequestFactory(okHttpClient);// 3. 构建 RestClient.Builder注入自定义 factoryRestClient.BuilderrestClientBuilderRestClient.builder().baseUrl(baseUrl).requestFactory(factory);// 4. 提供 WebClient.Builder必须传默认空 builder 即可WebClient.BuilderwebClientBuilderWebClient.builder();// 5. 调用 OllamaApi 实际构造函数returnnewOllamaApi(baseUrl,restClientBuilder,webClientBuilder);}}YAML 配置精简既然已经通过代码完全掌控了 HTTP 客户端超时就可以移除application.yml中的spring.restclient、spring.okhttp等无关超时配置保持清晰server:port:885logging:level:com.badao:debugorg.springframework.ai:debugspring:ai:ollama:base-url:http://localhost:11434chat:options:model:qwen2.5:7b-instructtemperature:0.5timeout:120s# 服务端模型生成超时依然建议保留embedding:options:model:nomic-embed-texttimeout:120sservlet:multipart:max-file-size:10MBmax-request-size:10MB关键要点读超时必须大于模型超时这里readTimeout 3 分钟而chat.options.timeout 2 分钟留有充足缓冲。OkHttp3ClientHttpRequestFactory过时警告不影响功能可忽略。如需消除需整体切换到其他 HTTP 客户端如 JDK HttpClient但会增加配置复杂度不值得。不要添加额外的 YAML OkHttp 配置避免干扰。五、验证效果重新编译并启动应用。发送之前会导致超时的 RAG 请求。观察日志不再出现Read timed out或SocketTimeoutException。模型正常返回生成结果即使耗时超过 10 秒、甚至 1 分钟也能顺利完成。六、总结本次问题的本质是Spring AI Ollama 使用的底层 OkHttp 读超时默认过短且 YAML 配置中的服务端超时选项无法控制客户端行为加上 Spring Boot 全局 OkHttp 属性与 Ollama 自动配置并不互通导致常规配置尝试全部失效。最终通过自定义OllamaApiBean 直接构建带超时的OkHttpClient并依其正确的构造函数注入彻底解决了超时问题。该方案稳定可靠推荐遇到同类问题的开发者采用。
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 客户端为 OkHttpSpring AI Ollama 在无自定义配置时默认通过OkHttp3ClientHttpRequestFactory使用 OkHttp 发起请求。OkHttp 的默认读超时为10 秒这就是超时发生在 10 秒的根本原因。三、根因分析1. 两层超时机制相互独立模型层超时chat.options.timeout该值会被序列化到POST /api/chat请求体中的options.timeout字段用于告知 Ollama 服务端允许的最长生成时间。服务端如果超时会主动中断生成并返回错误。HTTP 客户端层超时OkHttp 读超时这是 Java 应用等待服务器返回响应的最大时间。如果服务端处理慢比如模型生成耗时较长客户端会在达到读超时后直接抛出SocketTimeoutException无论服务端是否仍在正常工作。OkHttp 默认readTimeout 10_000ms10 秒。只有 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 管理的OkHttpClientBean而是直接创建了一个默认OkHttpClient因此全局配置同样不生效。此外若在 YAML 中不慎写出两个顶级spring:键会触发DuplicateKeyException导致启动失败。4. 自定义 Bean 时的常见坑直接创建OllamaApiBean 时需注意其构造函数签名在 1.0.0-M6 版本中为publicOllamaApi(StringbaseUrl,RestClient.BuilderrestClientBuilder,WebClient.BuilderwebClientBuilder)而不是(String, RestClient)。错误地调用构造函数会导致编译失败。四、最终解决方案自定义OllamaApiBean显式控制 OkHttp 超时直接通过配置类覆盖OllamaApiBean创建一个具有足够长读超时的OkHttpClient并将其通过RestClient.Builder注入到OllamaApi中。此方案完全绕过 Spring 的全局 OkHttp 配置从根源上解决问题。步骤在项目中新增配置类OllamaTimeoutConfig.java。使用Value注入spring.ai.ollama.base-url。构建自定义超时的OkHttpClient。创建RestClient.Builder并设置OkHttp3ClientHttpRequestFactory虽然已过时但功能正常可忽略警告。提供空WebClient.Builder实例。调用正确的OllamaApi三参数构造器并返回 Bean。完整代码packagecom.badao.ai.config;importokhttp3.OkHttpClient;importorg.springframework.ai.ollama.api.OllamaApi;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.http.client.OkHttp3ClientHttpRequestFactory;importorg.springframework.web.client.RestClient;importorg.springframework.web.reactive.function.client.WebClient;importjava.time.Duration;ConfigurationpublicclassOllamaTimeoutConfig{Value(${spring.ai.ollama.base-url})privateStringbaseUrl;BeanpublicOllamaApiollamaApi(){// 1. 自定义 OkHttpClient 超时OkHttpClientokHttpClientnewOkHttpClient.Builder().connectTimeout(Duration.ofSeconds(30))// 连接超时.readTimeout(Duration.ofMinutes(3))// 读超时 3 分钟大于模型 timeout.writeTimeout(Duration.ofSeconds(60))// 写超时.build();// 2. 创建 OkHttp3ClientHttpRequestFactory已过时但可用OkHttp3ClientHttpRequestFactoryfactorynewOkHttp3ClientHttpRequestFactory(okHttpClient);// 3. 构建 RestClient.Builder注入自定义 factoryRestClient.BuilderrestClientBuilderRestClient.builder().baseUrl(baseUrl).requestFactory(factory);// 4. 提供 WebClient.Builder必须传默认空 builder 即可WebClient.BuilderwebClientBuilderWebClient.builder();// 5. 调用 OllamaApi 实际构造函数returnnewOllamaApi(baseUrl,restClientBuilder,webClientBuilder);}}YAML 配置精简既然已经通过代码完全掌控了 HTTP 客户端超时就可以移除application.yml中的spring.restclient、spring.okhttp等无关超时配置保持清晰server:port:885logging:level:com.badao:debugorg.springframework.ai:debugspring:ai:ollama:base-url:http://localhost:11434chat:options:model:qwen2.5:7b-instructtemperature:0.5timeout:120s# 服务端模型生成超时依然建议保留embedding:options:model:nomic-embed-texttimeout:120sservlet:multipart:max-file-size:10MBmax-request-size:10MB关键要点读超时必须大于模型超时这里readTimeout 3 分钟而chat.options.timeout 2 分钟留有充足缓冲。OkHttp3ClientHttpRequestFactory过时警告不影响功能可忽略。如需消除需整体切换到其他 HTTP 客户端如 JDK HttpClient但会增加配置复杂度不值得。不要添加额外的 YAML OkHttp 配置避免干扰。五、验证效果重新编译并启动应用。发送之前会导致超时的 RAG 请求。观察日志不再出现Read timed out或SocketTimeoutException。模型正常返回生成结果即使耗时超过 10 秒、甚至 1 分钟也能顺利完成。六、总结本次问题的本质是Spring AI Ollama 使用的底层 OkHttp 读超时默认过短且 YAML 配置中的服务端超时选项无法控制客户端行为加上 Spring Boot 全局 OkHttp 属性与 Ollama 自动配置并不互通导致常规配置尝试全部失效。最终通过自定义OllamaApiBean 直接构建带超时的OkHttpClient并依其正确的构造函数注入彻底解决了超时问题。该方案稳定可靠推荐遇到同类问题的开发者采用。