摸索AI(四)Spring AI中的Http配置
Spring Aiadmin 发布于:2026-03-12 09:32:12
阅读:loading
之前也是花了巨多的时间从Github上下载了一些AI摸索的实践项目,涉及到的有文生图、文生视频、图生图、语音克隆、数字人等,它们的实践对电脑配置的依赖有一定的要求,而且实践的过程复杂程度较高,属于摸索着玩玩而已。本次摸索AI的范围则是面向对擅长领域的代码接入实践。如果你对Java代码交互的AI大模型有一些兴趣,又或者是跟我一样不知从哪里入手,或许看看我这里分享的前后实践过程与实践的案例范围,也是不错的选择。
所以,本系列教程相关的实践是在本地部署大模型,并且使用Java代码与本地的大模型进行交互。除了本地部署的大模型以为,也是可以付费接入网络上的一些付费大模型,比如Deepseek、千问等等,但是对我个人来讲,私有化的本地大模型更加有意义,毕竟可以免费的集成到企业级应用实践当中。
本次实践围绕使用Spring AI在企业级应用中的专业层面进行的实践,主要考虑Http请求配置的角度。Spring的AI本质上还是接口调用,有使用RestTemplate和RestClient等形式,同时考虑的细节有:Http连接池、各种超时时间、HttpClient、拦截器等等。
1.RestTemplate
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
//设置请求配置
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
//最大连接数
connectionManager.setMaxTotal(PoolingHttpClientConnectionManager.DEFAULT_MAX_TOTAL_CONNECTIONS);
//每个主机的最大连接数
connectionManager.setDefaultMaxPerRoute(PoolingHttpClientConnectionManager.DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
//设置Socket超时时间
SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(Timeout.ofSeconds(30)).build();
connectionManager.setDefaultSocketConfig(socketConfig);
//设置Connection Config
ConnectionConfig connectionConfig = ConnectionConfig.custom()
//Socket 读取超时 连接在连接池中的最大存活时间
.setTimeToLive(Timeout.ofSeconds(60))
//建立连接的超时
.setConnectTimeout(Timeout.ofSeconds(10))
.build();
connectionManager.setDefaultConnectionConfig(connectionConfig);
RequestConfig requestConfig = RequestConfig.custom()
//整个请求最大允许耗时时间
.setResponseTimeout(Timeout.ofSeconds(300))
//从连接池获取连接的最大等待时间
.setConnectionRequestTimeout(Timeout.ofSeconds(5))
.build();
//创建 HttpClient 实例
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(requestConfig).build();
//创建RestTemplate
final RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
restTemplate.setInterceptors(Collections.singletonList(new LoggingRequestInterceptor()));
return restTemplate;
}
}public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
//记录请求日志
this.logRequest(request , body);
ClientHttpResponse response = execution.execute(request, body);
CacheClientHttpResponseWrapper responseWapper;
try (InputStream inputStream = response.getBody()) {
final byte[] cacheBody = StreamUtils.copyToByteArray(inputStream);
responseWapper = new CacheClientHttpResponseWrapper(response , cacheBody);
}
//记录响应日志
this.logResponse(request , responseWapper);
return responseWapper;
}
private void logResponse(HttpRequest request, ClientHttpResponse response) throws IOException {
final MediaType contentType = request.getHeaders().getContentType();
StringBuilder responseBuilder = new StringBuilder();
boolean text = contentType != null &&
(
contentType.isCompatibleWith(MediaType.APPLICATION_JSON)
||
contentType.isCompatibleWith(MediaType.APPLICATION_XML)
||
contentType.toString().contains("text/")
);
responseBuilder.append("=== Response Begin ===\n");
responseBuilder.append("URI :").append(request.getURI()).append("\n");
responseBuilder.append("Status Code :").append(response.getStatusCode().value()).append("\n");
responseBuilder.append("Status Text :").append(response.getStatusText()).append("\n");
if (text) {
responseBuilder.append("Body :").append(StreamUtils.copyToString(response.getBody() , StandardCharsets.UTF_8)).append("\n");
} else {
responseBuilder.append("Body :[Binary response,").append(" bytes, skipped for logging]\n");
}
responseBuilder.append("=== Response End ===\n");
System.out.println(responseBuilder.toString());
}
private void logRequest(HttpRequest request, byte[] body) {
final MediaType contentType = request.getHeaders().getContentType();
StringBuilder requestBuilder = new StringBuilder();
boolean text = contentType != null &&
(
contentType.isCompatibleWith(MediaType.APPLICATION_JSON)
||
contentType.isCompatibleWith(MediaType.APPLICATION_XML)
||
contentType.toString().contains("text/")
);
requestBuilder.append("=== Request Begin ===\n");
requestBuilder.append("URI :").append(request.getURI()).append("\n");
requestBuilder.append("Method :").append(request.getMethod()).append("\n");
requestBuilder.append("Headers :").append(request.getHeaders()).append("\n");
if (text) {
requestBuilder.append("Body :").append(new String(body , StandardCharsets.UTF_8)).append("\n");
} else {
requestBuilder.append("Body :[Binary data, size: ]").append(body.length).append(" bytes, skipped for logging]\n");
}
requestBuilder.append("=== Request End ===\n");
System.out.println(requestBuilder.toString());
}
}3.其它说明
(1)本次实践的代码是Http请求中常见的设置,准确来说与Spring AI关系不大,使用Spring的Http接口交互时的常用设置;
(2)许多案例给出的是结合Spring Boot后的配置注入方式,这种把大模型的服务URL、模型名称等等参数细节配置在application配置文件中的形式,我感觉不够灵活,后续相关的示例均使用硬编码的形式给出,面向底层代码更加便于新手水平理解掌握;
点赞
欢迎来到陈冬冬的学习天地 | 学习与经验整理分享平台