目錄
- 一、背景
- 二、排查過程
- 三、解決方法
- 四、學習與思考-響應壓縮
- (一)可能原因
- (二)深入排查
- (三)注意
一、背景
接口發布到測試環境,測試同學說沒有數據
二、排查過程
1、本地用相同的參數、相同的庫、相同的代碼分支,發現接口正常且有響應數據
2、懷疑是不是運維配置操作導致不是一個數據庫
3、排查所有可能用到的測試庫以及分支,發現接口還是正常的
4、實際去測試環境看接口請求響應,發現接口是200通的,但是response是空的,說明沒有響應體
5、測試環境:請求接口對應的sprinboot項目服務是通過nginx轉發的
最終懷疑是響應nginx攔截了
三、解決方法
- springboot項目的yml配置文件如下
server:port: 55129servlet:context-path: /testcompression:mime-types: application/jsonenabled: true
- 這里的true改為false,問題解決了
四、學習與思考-響應壓縮
(一)可能原因
- 客戶端不支持壓縮:客戶端(像瀏覽器或者其他調用接口的應用程序)可能不支持服務器所采用的壓縮格式(通常是 gzip 或者
deflate)。當客戶端接收到壓縮后的響應體時,無法正確對其進行解壓縮,從而造成無法解析響應數據。 - 代理服務器問題:Nginx 作為代理服務器,可能沒有正確處理壓縮響應。例如,在轉發響應時,Nginx
可能移除了與壓縮相關的請求頭或者響應頭,使得客戶端無法識別響應是經過壓縮的,進而無法正確解析。 - 壓縮配置錯誤:Spring Boot
的壓縮配置可能存在錯誤,比如壓縮算法不兼容、壓縮級別設置不合理等,導致壓縮后的響應體無法被客戶端正確解析。
(二)深入排查
1、檢查客戶端請求頭:要確保客戶端在請求頭中包含 Accept-Encoding 字段,并且其值為 gzip, deflate,以此表明客戶端支持這些壓縮格式。例如,在使用 HttpClient 發送請求時,可以這樣設置請求頭:
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;public class HttpClientExample {public static void main(String[] args) throws IOException, InterruptedException {HttpClient client = HttpClient.newBuilder().build();HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://your_domain_or_ip/bi/your_endpoint")).header("Accept-Encoding", "gzip, deflate").build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());}
}
2、配置 Nginx 支持壓縮:在 Nginx 配置文件中添加壓縮相關的配置,確保 Nginx 能夠正確處理壓縮響應。示例配置如下:
server {listen 80;server_name your_domain_or_ip;gzip on;gzip_types application/json text/html text/css application/javascript;location /bi {proxy_pass http://localhost:55129/bi;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header Accept-Encoding ""; # 移除客戶端的Accept-Encoding頭proxy_pass_header Content-Encoding; # 傳遞響應的Content-Encoding頭}
}
3、檢查 Spring Boot 壓縮配置:確保 Spring Boot 的壓縮配置正確,例如 mime-types 設置是否包含了你希望壓縮的響應類型。
server:port: 55129servlet:context-path: /testcompression:mime-types: application/json, text/html, text/css, application/javascriptenabled: true
4、與后端服務器壓縮配置沖突:如果后端服務器(如 Spring Boot 應用)配置了響應壓縮,而 Nginx 沒有相應配置,可能會出現一些問題。例如,Nginx 可能會在轉發響應時移除或修改與壓縮相關的請求頭和響應頭,導致客戶端無法正確識別和處理壓縮后的數據。
移除或修改請求頭
后端服務器在收到客戶端請求時,會檢查請求頭中的Accept-Encoding字段,以確定客戶端支持的壓縮算法。如果客戶端支持gzip或deflate等壓縮算法,后端服務器會對響應數據進行壓縮,并在響應頭中添加Content-Encoding字段,告知客戶端響應數據的壓縮方式。
然而,Nginx
在轉發請求時,如果沒有配置處理壓縮相關的請求頭,可能會默認移除Accept-Encoding請求頭。這樣,后端服務器就無法得知客戶端支持的壓縮算法,可能會導致后端服務器對原本可以壓縮的數據不進行壓縮,或者采用客戶端不支持的壓縮算法進行壓縮。
移除或修改響應頭
后端服務器將壓縮后的響應數據發送給 Nginx,Nginx
在轉發響應時,如果沒有配置處理壓縮相關的響應頭,可能會移除Content-Encoding響應頭。客戶端接收響應時,由于沒有Content-Encoding頭信息,無法得知響應數據是經過壓縮的,會按照未壓縮的數據格式進行解析,導致解析錯誤。
另外,Nginx
也可能會修改Content-Length響應頭。因為壓縮后的數據長度通常會小于原始數據長度,后端服務器會根據壓縮后的數據長度設置Content-Length頭。但
Nginx
如果不了解壓縮情況,可能會錯誤地修改Content-Length頭為原始數據長度,導致客戶端在讀取響應數據時,按照錯誤的長度進行讀取,從而出現數據不完整或解析錯誤的情況。
- 我們項目實際上就是nginx沒有配置響應壓縮,而springboot項目配置了響應壓縮,導致接口沒有響應體。
(三)注意
當前把 enabled 設為 false 雖然解決了問題,但這樣會使響應壓縮功能失效,在傳輸大文件或者大量數據時,會增加網絡帶寬的占用,降低數據傳輸的速度。