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协商相关请求头,可以正常调用模型。但是还有一点要注意,当classpath中包含其他http客户端时,可能会采用其他ClientHttpRequestFactoryBuilder,详见ClientHttpRequestFactoryBuilder#detect,这时可能需要修改HttpClientConfig的泛型类型并重写customize方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921137.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921137.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rust学习之开发环境

工具链 安装 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh确认 ethanG5000:~$ rustc --version rustc 1.89.0 (29483883e 2025-08-04)创建工程 创建 cargo new demo上述&#xff0c;demo为工程名称。 调试 cargo run静态编译 目前计划使用rust编写一些小工具。…

计算机毕业设计选题推荐:基于Python+Django的新能源汽车数据分析系统

精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、项目介绍二…

MATLAB矩阵及其运算(三)矩阵的创建

3.1 元素输入法元素输入法是最简单&#xff0c;也是最常用的一种矩阵的生成方法。例如&#xff1a;注意&#xff1a;整个矩阵必须用“[]”括起来&#xff1b;元素之间必须用逗号“&#xff0c;”或空格分开&#xff1b;矩阵的行与行之间必须用“&#xff1b;”或者回车键“Ente…

JVM分析(OOM、死锁、死循环)(JProfiler、arthas、jdk调优工具(命令行))

JVM分析&#xff08;OOM、死锁、死循环&#xff09;&#xff08;JProfiler、arthas、jdk调优工具&#xff08;命令行&#xff09;&#xff09; 本文声明&#xff1a; 以下内容均为 JDK 8 springboot 2.6.13 &#xff08;windows 11 或 CentOS 7.9.2009 &#xff09;进行 ssh连…

深度学习中的数据增强实战:基于PyTorch的图像分类任务优化

在深度学习的图像分类任务中&#xff0c;我们常常面临一个棘手的问题&#xff1a;训练数据不足。无论是小样本场景还是模型需要更高泛化能力的场景&#xff0c;单纯依靠原始数据训练的模型很容易陷入过拟合&#xff0c;导致在新数据上的表现不佳。这时候&#xff0c;数据增强&a…

IEEE 802.11 MAC架构解析:DCF与HCF如何塑造现代Wi-Fi网络?

IEEE 802.11 MAC架构解析:DCF与HCF如何塑造现代Wi-Fi网络? 你是否曾好奇,当多个设备同时连接到同一个Wi-Fi网络时,它们是如何避免数据冲突并高效共享无线信道的?这背后的核心秘密就隐藏在IEEE 802.11标准的MAC(媒体访问控制)子层架构中。今天,我们将深入解析这一架构的…

深入掌握sed:Linux文本处理的流式编辑器利器

一、前言&#xff1a;sed是什么&#xff1f; 二、sed的工作原理 数据处理流程&#xff1a; 详细工作流程&#xff1a; 三、sed命令常见用法 基本语法&#xff1a; 常用选项&#xff1a; 常用操作命令&#xff1a; 四、实用示例演示 1. 输出符合条件的文本&#xff08;…

k8s三阶段项目

k8s部署discuz论坛和Tomcat商城 一、持久化存储—storageclassnfs 1.创建sa账户 [rootk8s-master scnfs]# cat nfs-provisioner-rbac.yaml # 1. ServiceAccount&#xff1a;供 NFS Provisioner 使用的服务账号 apiVersion: v1 kind: ServiceAccount metadata:name: nfs-prov…

Zynq开发实践(FPGA之流水线和冻结)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】谈到fpga相比较cpu的优势&#xff0c;很多时候我们都会谈到数据并发、边接收边处理、流水线这三个方面。所以&#xff0c;第三个优势&#xff0c;也…

接口保证幂等性你学废了吗?

接口幂等性定义&#xff1a;无论一次或多次调用某个接口&#xff0c;对资源产生的副作用都是一致的。 简单来说&#xff1a;用户由于各种原因&#xff08;网络超时、前端重复点击、消息重试等&#xff09;对同一个接口发了多次请求&#xff0c;系统只能处理一次&#xff0c;不能…

入行FPGA选择国企、私企还是外企?

不少人想要转行FPGA&#xff0c;但不知道该如何选择公司&#xff1f;下面就来为大家盘点一下FPGA大厂的薪资和工作情况&#xff0c;欢迎大家在评论区补充。一、老牌巨头在 FPGA设计 领域深耕许久&#xff0c;流程完善、技术扎实&#xff0c;公司各项制度都很完善&#xff0c;前…

考研总结,25考研京区上岸总结(踩坑和建议)

我的本科是一所普通的双非&#xff0c;其实&#xff0c;从我第一天入学时候&#xff0c;我就想走出去&#xff0c;开学给我带来的更多是失望&#xff08;感觉自己高考太差劲了&#xff09;&#xff0c;是不甘心&#xff08;自己一定可以去更好的地方&#xff09;。我在等一次机…

基于数据挖掘的当代不孕症医案证治规律研究

标题:基于数据挖掘的当代不孕症医案证治规律研究内容:1.摘要 背景&#xff1a;随着现代生活方式的改变&#xff0c;不孕症的发病率呈上升趋势&#xff0c;为探索有效的中医证治规律&#xff0c;数据挖掘技术为其提供了新的途径。目的&#xff1a;运用数据挖掘方法研究当代不孕症…

《sklearn机器学习》——调整估计器的超参数

GridSearchCV 详解&#xff1a;网格搜索与超参数优化 GridSearchCV 是 scikit-learn 中用于超参数调优的核心工具之一。它通过系统地遍历用户指定的参数组合&#xff0c;使用交叉验证评估每种组合的性能&#xff0c;最终选择并返回表现最优的参数配置。这种方法被称为网格搜索&…

一站式可视化运维:解锁时序数据库 TDengine 的正确打开方式

小T导读&#xff1a;运维数据库到底有多复杂&#xff1f;从系统部署到数据接入&#xff0c;从权限配置到监控告警&#xff0c;动辄涉及命令行、脚本和各种文档查找&#xff0c;一不留神就可能“翻车”。为了让 TDengine 用户轻松应对这些挑战&#xff0c;我们推出了《TDengine …

多线程同步安全机制

目录 以性能换安全 1.synchronized 同步 &#xff08;1&#xff09;不同的对象竞争同一个资源&#xff08;锁得住&#xff09; &#xff08;2&#xff09;不同的对象竞争不同的资源&#xff08;锁不住&#xff09; &#xff08;3&#xff09;单例模式加锁 synchronized …

多路复用 I/O 函数——`select`函数

好的&#xff0c;我们以 Linux 中经典的多路复用 I/O 函数——select 为例&#xff0c;进行一次完整、深入且包含全部代码的解析。 <摘要> select 是 Unix/Linux 系统中传统的多路复用 I/O 系统调用。它允许一个程序同时监视多个文件描述符&#xff08;通常是套接字&…

嵌入式碎片知识总结(二)

1.repo的一个问题&#xff1a;repo init -u ssh://shchengerrit.bouffalolab.com:29418/bouffalo/manifest/bouffalo_sdk -b master -m allchips-internal.xml /usr/bin/repo:681: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in…

java中二维数组笔记

课程链接:黑马程序员java零基础[上] 1.二维数组的内存分布 在 Java 中&#xff0c;二维数组并不是一整块连续的二维空间&#xff0c;而是数组的数组。具体而言,在声明一个二维数组&#xff1a;如int[][] arr new int[2][3];时&#xff0c;内存中会发生如下: 1.1 栈上的引用变…

系统架构设计师备考第13天——计算机语言-多媒体

一、多媒体基础概念媒体的分类 感觉媒体&#xff1a;人类感官直接接收的信息形式&#xff08;如声音、图像&#xff09;。表示媒体&#xff1a;信息的数字化表示&#xff08;如JPEG图像、MP3音频&#xff09;。显示媒体&#xff1a;输入/输出设备&#xff08;如键盘、显示器&am…