国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Spring Boot+Filter實現(xiàn)Gzip壓縮超大json對象

jf_ro2CN3Fa ? 來源:芋道源碼 ? 作者:芋道源碼 ? 2022-12-01 10:18 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群


1. 業(yè)務(wù)背景

是這樣的,業(yè)務(wù)背景是公司的內(nèi)部系統(tǒng)有一個廣告保存接口,需要ADX那邊將投放的廣告數(shù)據(jù)進行保存供后續(xù)使用。 廣告數(shù)據(jù)大概長這樣:

  • adName是廣告名字
  • adTag是廣告渲染的HTML代碼,超級大數(shù)據(jù)庫中都是用text類型來存放的,我看到最大的adTag足足有60kb大小…
{
"adName":"",
"adTag":""
}

因此,對與請求數(shù)據(jù)那么大的接口我們肯定是需要作一個優(yōu)化的否則太大的數(shù)據(jù)傳輸有以下幾個弊端:

  • 占用網(wǎng)絡(luò)帶寬,而有些云產(chǎn)品就是按照帶寬來計費的,間接浪費了錢
  • 傳輸數(shù)據(jù)大導(dǎo)致網(wǎng)絡(luò)傳輸耗時

為了克服這幾個問題團隊中的老鳥產(chǎn)生一個想法:

請求廣告保存接口時先將Json對象字符串進行GZIP壓縮,那請求時傳入的就是壓縮后的數(shù)據(jù),而GZIP的壓縮效率是很高的,因此可以大大減小傳輸數(shù)據(jù),而當(dāng)數(shù)據(jù)到達廣告保存接口前再將傳來的數(shù)據(jù)進行解壓縮,還原成JSON對象就完成了整個GZIP壓縮數(shù)據(jù)的請求以及處理流程。

其實這樣做也存在著弊端:

  • 請求變復(fù)雜了

    • 接口調(diào)用方那邊需要對數(shù)據(jù)進行壓縮
    • 接口執(zhí)行方那邊需要對拿到的數(shù)據(jù)進行解壓
  • 需要額外占用更多的CPU計算資源

  • 可能會影響到原有的其他接口

對于以上幾點基于我們公司當(dāng)前的業(yè)務(wù)可以這樣解決:

  • 對與需要占用而外的CPU計算資源來說,公司的內(nèi)部系統(tǒng)屬于IO密集型應(yīng)用,因此用一些CPU資源來換取更快的網(wǎng)絡(luò)傳輸其實是很劃算的
  • 使用過濾器在請求數(shù)據(jù)到達Controller之前對數(shù)據(jù)進行解壓縮處理后重新寫回到Body中,避免影響Controller的邏輯,代碼零侵入
  • 而對于改造接口的同時是否會影響到原來的接口這一點可以通過 HttpHeader 的Content-Encoding=gzip屬性來區(qū)分是否需要對請求數(shù)據(jù)進行解壓縮

那廢話少說,下面給出實現(xiàn)方案

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

2. 實現(xiàn)思路

前置知識:
  • Http 請求結(jié)構(gòu)以及Content-Encoding 屬性
  • gzip壓縮方式
  • Servlet Filter
  • HttpServletRequestWrapper
  • Spring Boot
  • Java 輸入輸出流

實現(xiàn)流程圖:

e6600140-711c-11ed-8abf-dac502259ad0.png
核心代碼:

創(chuàng)建一個SpringBoot項目,先編寫一個接口,功能很簡單就是傳入一個Json對象并返回,以模擬將廣告數(shù)據(jù)保存到數(shù)據(jù)庫

/**
*@ClassName:ProjectController
*@Authorzhangjin
*@Date2022/3/2420:41
*@Description:
*/
@Slf4j
@RestController
publicclassAdvertisingController{

@PostMapping("/save")
publicAdvertisingsaveProject(@RequestBodyAdvertisingadvertising){
log.info("獲取內(nèi)容"+advertising);
returnadvertising;
}
}

/**
*@ClassName:Project
*@Authorzhangjin
*@Date2022/3/2420:42
*@Description:
*/
@Data
publicclassAdvertising{
privateStringadName;
privateStringadTag;
}

編寫并注冊一個攔截器

/**
*@ClassName:GZIPFilter
*@Authorzhangjin
*@Date2022/3/260:36
*@Description:
*/
@Slf4j
@Component
publicclassGZIPFilterimplementsFilter{

privatestaticfinalStringCONTENT_ENCODING="Content-Encoding";
privatestaticfinalStringCONTENT_ENCODING_TYPE="gzip";

@Override
publicvoidinit(FilterConfigfilterConfig)throwsServletException{
log.info("initGZIPFilter");
}

@Override
publicvoiddoFilter(ServletRequestservletRequest,ServletResponseservletResponse,FilterChainfilterChain)throwsIOException,ServletException{
longstart=System.currentTimeMillis();
HttpServletRequesthttpServletRequest=(HttpServletRequest)servletRequest;

StringencodeType=httpServletRequest.getHeader(CONTENT_ENCODING);
if(CONTENT_ENCODING_TYPE.equals(encodeType)){
log.info("請求:{}需要解壓",httpServletRequest.getRequestURI());
UnZIPRequestWrapperunZIPRequestWrapper=newUnZIPRequestWrapper(httpServletRequest);
filterChain.doFilter(unZIPRequestWrapper,servletResponse);
}
else{
log.info("請求:{}無需解壓",httpServletRequest.getRequestURI());
filterChain.doFilter(servletRequest,servletResponse);
}
log.info("耗時:{}ms",System.currentTimeMillis()-start);
}

@Override
publicvoiddestroy(){
log.info("destroyGZIPFilter");
}
}

/**
*@ClassName:FilterRegistration
*@Authorzhangjin
*@Date2022/3/260:36
*@Description:
*/
@Configuration
publicclassFilterRegistration{

@Resource
privateGZIPFiltergzipFilter;

@Bean
publicFilterRegistrationBeangzipFilterRegistrationBean(){
FilterRegistrationBeanregistration=newFilterRegistrationBean<>();
//Filter可以new,也可以使用依賴注入Bean
registration.setFilter(gzipFilter);
//過濾器名稱
registration.setName("gzipFilter");
//攔截路徑
registration.addUrlPatterns("/*");
//設(shè)置順序
registration.setOrder(1);
returnregistration;
}
}

實現(xiàn)RequestWrapper實現(xiàn)解壓和寫回Body的邏輯

/**
*@ClassName:UnZIPRequestWrapper
*@Authorzhangjin
*@Date2022/3/2611:02
*@Description:JsonString經(jīng)過壓縮后保存為二進制文件->解壓縮后還原成JsonString轉(zhuǎn)換成byte[]寫回body中
*/
@Slf4j
publicclassUnZIPRequestWrapperextendsHttpServletRequestWrapper{

privatefinalbyte[]bytes;

publicUnZIPRequestWrapper(HttpServletRequestrequest)throwsIOException{
super(request);
try(BufferedInputStreambis=newBufferedInputStream(request.getInputStream());
ByteArrayOutputStreambaos=newByteArrayOutputStream()){
finalbyte[]body;
byte[]buffer=newbyte[1024];
intlen;
while((len=bis.read(buffer))>0){
baos.write(buffer,0,len);
}
body=baos.toByteArray();
if(body.length==0){
log.info("Body無內(nèi)容,無需解壓");
bytes=body;
return;
}
this.bytes=GZIPUtils.uncompressToByteArray(body);
}catch(IOExceptionex){
log.info("解壓縮步驟發(fā)生異常!");
ex.printStackTrace();
throwex;
}
}

@Override
publicServletInputStreamgetInputStream()throwsIOException{
finalByteArrayInputStreambyteArrayInputStream=newByteArrayInputStream(bytes);
returnnewServletInputStream(){

@Override
publicbooleanisFinished(){
returnfalse;
}

@Override
publicbooleanisReady(){
returnfalse;
}

@Override
publicvoidsetReadListener(ReadListenerreadListener){

}

publicintread()throwsIOException{
returnbyteArrayInputStream.read();
}
};
}

@Override
publicBufferedReadergetReader()throwsIOException{
returnnewBufferedReader(newInputStreamReader(this.getInputStream()));
}

}

附上壓縮工具類

publicclassGZIPUtils{

publicstaticfinalStringGZIP_ENCODE_UTF_8="UTF-8";

/**
*字符串壓縮為GZIP字節(jié)數(shù)組
*@paramstr
*@return
*/
publicstaticbyte[]compress(Stringstr){
returncompress(str,GZIP_ENCODE_UTF_8);
}

/**
*字符串壓縮為GZIP字節(jié)數(shù)組
*@paramstr
*@paramencoding
*@return
*/
publicstaticbyte[]compress(Stringstr,Stringencoding){
if(str==null||str.length()==0){
returnnull;
}
ByteArrayOutputStreamout=newByteArrayOutputStream();
GZIPOutputStreamgzip;
try{
gzip=newGZIPOutputStream(out);
gzip.write(str.getBytes(encoding));
gzip.close();
}catch(IOExceptione){
e.printStackTrace();
}
returnout.toByteArray();
}

/**
*GZIP解壓縮
*@parambytes
*@return
*/
publicstaticbyte[]uncompress(byte[]bytes){
if(bytes==null||bytes.length==0){
returnnull;
}
ByteArrayOutputStreamout=newByteArrayOutputStream();
ByteArrayInputStreamin=newByteArrayInputStream(bytes);
try{
GZIPInputStreamungzip=newGZIPInputStream(in);
byte[]buffer=newbyte[256];
intn;
while((n=ungzip.read(buffer))>=0){
out.write(buffer,0,n);
}
}catch(IOExceptione){
e.printStackTrace();
}
returnout.toByteArray();
}

/**
*解壓并返回String
*@parambytes
*@return
*/
publicstaticStringuncompressToString(byte[]bytes)throwsIOException{
returnuncompressToString(bytes,GZIP_ENCODE_UTF_8);
}

/**
*
*@parambytes
*@return
*/
publicstaticbyte[]uncompressToByteArray(byte[]bytes)throwsIOException{
returnuncompressToByteArray(bytes,GZIP_ENCODE_UTF_8);
}

/**
*解壓成字符串
*@parambytes壓縮后的字節(jié)數(shù)組
*@paramencoding編碼方式
*@return解壓后的字符串
*/
publicstaticStringuncompressToString(byte[]bytes,Stringencoding)throwsIOException{
byte[]result=uncompressToByteArray(bytes,encoding);
returnnewString(result);
}

/**
*解壓成字節(jié)數(shù)組
*@parambytes
*@paramencoding
*@return
*/
publicstaticbyte[]uncompressToByteArray(byte[]bytes,Stringencoding)throwsIOException{
if(bytes==null||bytes.length==0){
returnnull;
}
ByteArrayOutputStreamout=newByteArrayOutputStream();
ByteArrayInputStreamin=newByteArrayInputStream(bytes);
try{
GZIPInputStreamungzip=newGZIPInputStream(in);
byte[]buffer=newbyte[256];
intn;
while((n=ungzip.read(buffer))>=0){
out.write(buffer,0,n);
}
returnout.toByteArray();
}catch(IOExceptione){
e.printStackTrace();
thrownewIOException("解壓縮失敗!");
}
}

/**
*將字節(jié)流轉(zhuǎn)換成文件
*@paramfilename
*@paramdata
*@throwsException
*/
publicstaticvoidsaveFile(Stringfilename,byte[]data)throwsException{
if(data!=null){
Stringfilepath="/"+filename;
Filefile=newFile(filepath);
if(file.exists()){
file.delete();
}
FileOutputStreamfos=newFileOutputStream(file);
fos.write(data,0,data.length);
fos.flush();
fos.close();
System.out.println(file);
}
}

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

3. 測試效果

注意一個大坑:千萬不要直接將壓縮后的byte[]當(dāng)作字符串進行傳輸,否則你會發(fā)現(xiàn)壓縮后的請求數(shù)據(jù)竟然比沒壓縮后的要大得多!一般有兩種傳輸壓縮后的byte[]的方式:

  • 將壓縮后的byet[]進行base64編碼再傳輸字符串,這種方式會損失掉一部分GZIP的壓縮效果,適用于壓縮結(jié)果要存儲在Redis中的情況
  • 將壓縮后的byte[]以二進制的形式寫入到文件中,請求時直接在body中帶上文件即可,用這種方式可以不損失壓縮效果

Postman測試Gzip壓縮數(shù)據(jù)請求:

  • 請求頭指定數(shù)據(jù)壓縮方式:
e66cba2a-711c-11ed-8abf-dac502259ad0.png
  • Body帶上壓縮后的byte[]寫入的二進制文件
e67736f8-711c-11ed-8abf-dac502259ad0.png
  • 執(zhí)行請求,服務(wù)端正確處理了請求并且請求size縮小了將近一半,效果還是很不錯的,這樣GZIP壓縮數(shù)據(jù)的請求的處理就完成了,完整的項目代碼在下方
e69f980a-711c-11ed-8abf-dac502259ad0.png

4. Demo地址

  • https://gitee.com/wx_1bceb446a4/gziptest


審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    341

    瀏覽量

    15935
  • 傳輸數(shù)據(jù)
    +關(guān)注

    關(guān)注

    1

    文章

    128

    瀏覽量

    16505
  • 大數(shù)據(jù)
    +關(guān)注

    關(guān)注

    64

    文章

    9063

    瀏覽量

    143743

原文標(biāo)題:Spring Boot + Filter 實現(xiàn) Gzip 壓縮超大 json 對象,傳輸耗時大大減少

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    JSON:簡潔代碼高效搞定序列化與反序列化

    面對頻繁的數(shù)據(jù)交互需求,用最簡方式實現(xiàn)JSON序列化與反序列化已成為開發(fā)者必備技能,借助主流庫,輕松實現(xiàn)零負擔(dān)數(shù)據(jù)轉(zhuǎn)換。JSON(JavaScriptObjectNotation)是一
    的頭像 發(fā)表于 02-25 19:04 ?90次閱讀
    <b class='flag-5'>JSON</b>:簡潔代碼高效搞定序列化與反序列化

    深入解析U-Boot image.c:RK平臺鏡像處理核心邏輯

    的SD/NAND/SPI等啟動方式做了專屬適配。本文將拆解image.c的核心邏輯,梳理RK平臺鏡像處理的關(guān)鍵流程,幫助開發(fā)者理解和調(diào)試啟動相關(guān)問題。 一、文件定位與核心作用 image.c是U-Boot中鏡像管理的核心模塊,主要負責(zé): ?定義uImage鏡像的屬性枚舉(架構(gòu)、系統(tǒng)、類型、
    的頭像 發(fā)表于 02-24 16:46 ?1445次閱讀
    深入解析U-<b class='flag-5'>Boot</b> image.c:RK平臺鏡像處理核心邏輯

    極簡代碼,搞定JSON序列化與反序列化

    快速實現(xiàn)JSON數(shù)據(jù)的生成(序列化)與解析(反序列化)。 目前json庫已全面支持LuatOS開發(fā)系列產(chǎn)品,開發(fā)者可根據(jù)項目實際需求,靈活選用并進行快速集成與開發(fā)。 一、 JSON基礎(chǔ)
    的頭像 發(fā)表于 02-23 21:46 ?327次閱讀
    極簡代碼,搞定<b class='flag-5'>JSON</b>序列化與反序列化

    解析Rockchip平臺U-Boot核心文件:boot_rkimg.c到底做了什么?

    在嵌入式開發(fā)中,U-Boot 作為引導(dǎo)程序的 “中流砥柱”,負責(zé)初始化硬件、加載內(nèi)核并啟動系統(tǒng)。對于 Rockchip 平臺的設(shè)備(如常見的開發(fā)板、智能終端),boot_rkimg.c 是 U-Boot 中專門處理啟動流程的核心
    的頭像 發(fā)表于 02-03 15:29 ?743次閱讀
    解析Rockchip平臺U-<b class='flag-5'>Boot</b>核心文件:<b class='flag-5'>boot</b>_rkimg.c到底做了什么?

    Nginx Gzip壓縮配置指南

    說起Gzip壓縮,可能很多人覺得這是個老生常談的話題。但我在這幾年的運維工作中發(fā)現(xiàn),真正把Gzip配置到位的網(wǎng)站其實不多。去年幫一個客戶做性能優(yōu)化,他們的網(wǎng)站日均帶寬消耗在2TB左右,一看Nginx
    的頭像 發(fā)表于 01-30 16:03 ?258次閱讀

    從0到1搭建實時日志監(jiān)控系統(tǒng):基于WebSocket + Elasticsearch的實戰(zhàn)方案

    問題。 WebSocket斷連重試 :前端實現(xiàn)指數(shù)退避重連機制。 數(shù)據(jù)壓縮 :對大文本日志啟用Gzip壓縮,減少帶寬占用。 5. 最終效果 實時性 :日志從產(chǎn)生到展示延遲 < 1
    發(fā)表于 01-09 16:43

    詳解DBC的Signal與JSON文本結(jié)合

    為了優(yōu)化CAN數(shù)據(jù)發(fā)送與接收的操作流程,更改以前手動輸入狀態(tài)對應(yīng)數(shù)據(jù)的模式,采用下拉列表選擇內(nèi)容,但這需要用到超出DBC原有承載能力的信息。因此,將JSON與其結(jié)合,采用JSON格式文本寫入Signal的Comment屬性,將Comment屬性的字符串通過
    的頭像 發(fā)表于 01-06 10:57 ?297次閱讀
    詳解DBC的Signal與<b class='flag-5'>JSON</b>文本結(jié)合

    linux的壓縮和解壓操作

    對于重要的文件我們不會考慮這樣的方式。無損壓縮不會影響文件,所以對于壓縮我們最先考慮的是使用無損壓縮的方式。 2、 單個文件壓縮和解壓用 gzip
    發(fā)表于 12-23 06:56

    借助CXL和壓縮技術(shù)實現(xiàn)高效數(shù)據(jù)傳輸

    AI、科學(xué)計算、海量內(nèi)存處理……這些硬核工作負載正在不斷挑戰(zhàn)系統(tǒng)極限。而 FPGA 異軍突起,成為了實現(xiàn)高效數(shù)據(jù)傳輸?shù)摹瓣P(guān)鍵推手”。想知道怎么在不改變整體架構(gòu)的前提下,讓帶寬和能效實現(xiàn)“雙飛躍”?答案就藏在壓縮 IP 與基于 C
    的頭像 發(fā)表于 12-19 09:43 ?352次閱讀
    借助CXL和<b class='flag-5'>壓縮</b>技術(shù)<b class='flag-5'>實現(xiàn)</b>高效數(shù)據(jù)傳輸

    電能質(zhì)量在線監(jiān)測裝置的數(shù)據(jù)壓縮存儲功能支持的數(shù)據(jù)格式可以修改嗎?

    操作性與法律效力 壓縮算法 / 容器格式 ZLIB、GZIP、ZIP、LZ4 部分可配置 廠家會提供有限選擇
    的頭像 發(fā)表于 12-11 16:39 ?1090次閱讀
    電能質(zhì)量在線監(jiān)測裝置的數(shù)據(jù)<b class='flag-5'>壓縮</b>存儲功能支持的數(shù)據(jù)格式可以修改嗎?

    一款基于Java+Spring Boot+Vue的智慧隨訪管理系統(tǒng)源碼

    智慧隨訪管理系統(tǒng)源碼,一款基于Java+Spring Boot+Vue的B/S架構(gòu)醫(yī)院隨訪管理系統(tǒng)源碼,采用前后端分離技術(shù)(Ant-Design+MySQL5),具有自主版權(quán)和落地案例。 隨訪管理
    的頭像 發(fā)表于 11-13 15:38 ?397次閱讀
    一款基于Java+<b class='flag-5'>Spring</b> <b class='flag-5'>Boot</b>+Vue的智慧隨訪管理系統(tǒng)源碼

    優(yōu)化boot4的乘法運算周期

    可以在不同的時鐘周期內(nèi)完成,從而并行化運算流程,提高乘法器的運算性能。 采用多級壓縮:在Boot4乘法器中,使用了基于連乘算法的多級壓縮技術(shù)。可以通過增加多級壓縮,進一步降低管理乘法器
    發(fā)表于 10-21 13:17

    ulog_tag_lvl_filter_set()函數(shù)無法實現(xiàn)按照文檔說明那樣實現(xiàn)按模塊過濾,怎么解決?

    ulog_tag_lvl_filter_set()函數(shù)無法實現(xiàn)按照文檔說明那樣實現(xiàn)按模塊過濾,比如過濾掉所有drv.xxx的日志
    發(fā)表于 10-10 07:30

    基于FPGA的壓縮算法加速實現(xiàn)

    本設(shè)計中,計劃實現(xiàn)對文件的壓縮及解壓,同時優(yōu)化壓縮中所涉及的信號處理和計算密集型功能,實現(xiàn)對其的加速處理。本設(shè)計的最終目標(biāo)是證明在充分并行化的硬件體系結(jié)構(gòu) FPGA 上
    的頭像 發(fā)表于 07-10 11:09 ?2389次閱讀
    基于FPGA的<b class='flag-5'>壓縮</b>算法加速<b class='flag-5'>實現(xiàn)</b>

    不用聯(lián)網(wǎng)不用編程,PLC通過智能網(wǎng)關(guān)快速實現(xiàn)HTTP協(xié)議JSON格式與MES等系統(tǒng)平臺雙向數(shù)據(jù)通訊

    進行解析后將數(shù)據(jù)寫入到PLC,實現(xiàn)PLC與HTTP服務(wù)端雙向通訊;作為服務(wù)端時根據(jù)客戶端URL中的路徑查找所配置的數(shù)據(jù),打包成JSON文件后返回給客戶端。
    的頭像 發(fā)表于 05-13 14:40 ?1194次閱讀
    不用聯(lián)網(wǎng)不用編程,PLC通過智能網(wǎng)關(guān)快速<b class='flag-5'>實現(xiàn)</b>HTTP協(xié)議<b class='flag-5'>JSON</b>格式與MES等系統(tǒng)平臺雙向數(shù)據(jù)通訊