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
英文地址,請注明出處:http://en.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…