当前位置: 首页 > ai >正文

Spring AI调用sglang模型返回HTTP 400分析处理

Spring AI调用sglang模型返回HTTP 400分析处理

一、问题描述

环境
  • java21
  • springboot: 3.5.5
  • spring-ai: 1.0.1
问题描述

Spring AI调用公司部署的sglang大模型返回错误HTTP 400 - {"object":"error","message":[{'type': 'missing', 'loc': ('body',), 'msg': 'Field required', 'input': None}]","type":"Bad Request","param":null,"code":400},但调用公网模型没问题,使用postman调用内网模型也没问题。

二、分析解决

使用wireshark捕包对比Spring AI发出的请求和postman请求差异,发现Spring AI的请求多了请求头Transfer-Encoding: chunked,postman加上此请求头后也报了同样的错误,猜测是公司部署的sglang不支持分块传输。

观察异常堆栈,有一个exchange(DefaultRestClient.java:540),看名字应该是发送请求的入口,从这里打断点调试。

  1. 定位到583行的clientRequest.execute(),继续追踪,发现底层调用的是jdk提供的HttpClientImpl
  2. 这个客户端使用了大量的异步操作,先定位到Exchange#responseAsyncImpl0,然后定位到Http1Request#headers,可见由requestPublisher#contentLength决定是否为流式请求,当值为-1时添加请求头Transfer-Encoding: chunked。而且在JdkClientHttpRequest#buildRequest方法中,自动排除了connection、content-length、expect、host、upgrade几个请求头。
  3. 向前追踪,requestPublisher构建于JdkClientHttpRequest#bodyPublisher,当请求头中存在contentLength时,才会构建包含contentLength的requestPublisher。这里推测当请求体为固定大小时,会添加contentLength请求头。
  4. 回到DefaultRestClient#createRequest,这里有两种客户端构建方式,一种是存在拦截器时通过InterceptionClientHttpRequestFactory构建,另一种是通过默认的JdkClientHttpRequestFactory
  5. JdkClientHttpRequest继承自AbstractStreamingClientHttpRequest,请求体使用流式传输。InterceptionClientHttpRequestFactory继承自AbstractBufferingClientHttpRequest,请求体会完全缓存,在executeInternal方法中会自动添加Content-Length请求头。
  6. DefaultRestClient构造方法打断点,向上一步步找到DefaultRestClientBuilderRestClientAutoConfiguration#restClientBuilderRestClientBuilderConfigurerRestClientAutoConfiguration#restClientBuilderConfigurer,发现注入参数ObjectProvider<RestClientCustomizer> customizerProvider,于是自定义Bean如下。
    import org.springframework.boot.web.client.RestClientCustomizer;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestClient;@Configuration
    public class RestClientConfig implements RestClientCustomizer {@Overridepublic void customize(RestClient.Builder restClientBuilder) {restClientBuilder.requestInterceptor((request, body, execution) -> execution.execute(request, body));}
    }
    
  7. 此时请求头中已经添加了Content-Length,但还是报错。

再次使用wireshark捕包,发现请求中多了请求头Connection: UpgradeUpgrade: h2c来协商升级到HTTP2,推测应该是sglang服务端不支持。定位到ExchangeImpl#get,这里会判断需要使用的HTTP版本,进一步定位到MultiExchange#version,发现会依次获取request.version、client.version直到取到非空值。request中的version追踪后发现是空值且无法定制,于是尝试修改client.version。

  1. client为HttpClientImpl类,打断点追踪,由JdkHttpClientBuilder#build构建,并支持通过customizer进行自定义。
  2. 继续向上追踪,找到JdkClientHttpRequestFacotryBuilder#createClientHttpRequestFactoryAbstractClientHttpRequestFactoryBuilder#build,这里有一组customizers通过LambdaSafe#callbacksJdkClientHttpReuqestFactory进行自定义。
  3. AbstractClientHttpRequestFactoryBuilder构造方法打打断点,向上追踪, 找到HttpClientAutoConfiguration#clientHttpRequestFactoryBuilder,发现注入参数ObjectProvider<ClientHttpRequestFactoryBuilzer<?>> clientHttpRequestFactoryBuilderCustomizers,于是自定义Bean如下。
    import org.springframework.boot.autoconfigure.http.client.ClientHttpRequestFactoryBuilderCustomizer;
    import org.springframework.boot.http.client.JdkClientHttpRequestFactoryBuilder;
    import org.springframework.context.annotation.Configuration;import java.net.http.HttpClient;@Configuration
    public class HttpClientConfig implements ClientHttpRequestFactoryBuilderCustomizer<JdkClientHttpRequestFactoryBuilder> {@Overridepublic JdkClientHttpRequestFactoryBuilder customize(JdkClientHttpRequestFactoryBuilder builder) {return builder.withHttpClientCustomizer(httpClientBuilder -> httpClientBuilder.version(HttpClient.Version.HTTP_1_1));}
    }
    

再测试已无HTTP2协商相关请求头,可以正常调用模型。

http://www.xdnf.cn/news/19808.html

相关文章:

  • 前端学习 10-2 :验证中的SV
  • Qt使用Maintenance添加、卸载组件(未完)
  • Java 技术支撑 AI 系统落地:从模型部署到安全合规的企业级解决方案(四)
  • 嵌入式学习 51单片机(2)
  • 【C++】string类完全解析与实战指南
  • centos 压缩命令
  • (二)文件管理-基础命令-mkdir命令的使用
  • Linux应用(1)——文件IO
  • 部署jenkins并基于ansible部署Discuz应用
  • 嵌入式|RTOS教学——FreeRTOS基础3:消息队列
  • Unity之Spine动画资源导入
  • 小游戏公司接单难?这几点原因与破局思路值得看看
  • 聚焦诊断管理(DM)的传输层设计、诊断服务器实现、事件与通信管理、生命周期与报告五大核心模块
  • RTSP流端口占用详解:TCP模式与UDP模式的对比
  • 面向深层语义分析的公理化NLP模型:理论可行性、关键技术与应用挑战
  • 大语言模型领域最新进展
  • 如何将JPG图片批量转为PDF?其实可用的方法有很多种
  • TC-2024《Fuzzy Clustering guided by Spectral Rotation and Scaling》
  • shell-awk命令详解(理论+实战)
  • 通过IDEA写一个服务端和一个客户端之间的交互
  • 解决通过南瑞加密网关传输文件和推送视频的失败的问题
  • PyTorch 面试题及详细答案120题(116-120)-- 综合应用与实践
  • 专项智能练习(音频基础)
  • 水泵运行组态监控系统御控物联网解决方案
  • 基于SpringBoot的旅游管理系统
  • 03 - HTML常用标签
  • Nano Banana 的 100 种用法 - AI 图像生成完整提示词宝典
  • 超低延迟RTSP播放器的技术挑战与跨平台实现之道
  • 【GitOps】Argo CD部署应用程序
  • 嵌入式|RTOS教学——FreeRTOS基础2:任务调度