-
Notifications
You must be signed in to change notification settings - Fork 142
传输压缩的JSON
GeXiangDong edited this page Dec 15, 2018
·
3 revisions
有时客户端给服务器传输的JSON比较大,几兆甚至几十兆,网络传输占用时间较多,而json都是文本格式,压缩率较高。因此考虑传输压缩后的格式。
HTTP协议在相应部分支持Content-Encoding:gzip,压缩response body,而没有压缩request body的设计,这也很合理,因为在客户端发起请求时并不知道服务器是否支持压缩。因此没法通过http自身解决,只能增加些程序。
考虑到通用性,仿效response的header Content-Encoding:gzip方式。
客户端把压缩过的json作为post-body传输,然后增加一个request header: Content-Encoding: gzip来告诉服务器端是压缩的格式。
服务端增加一个Filter,对request头进行检查,如果有Content-Encoding则解压缩后继续。这样不影响现有程序。
服务端还有另外一个解决方案,在NGINX反向代理处增加一个插件(需要去找实现这种功能的插件,官方未提供)把解压缩后的request body传给应用服务器(tomcat)。
增加2个类:
package cn.devmgr.springcloud;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Service
public class ContentEncodingFilter extends OncePerRequestFilter {
Logger logger = LoggerFactory.getLogger(ContentEncodingFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String conentEncoding = request.getHeader("Content-Encoding");
if(conentEncoding != null && ("gzip".equalsIgnoreCase(conentEncoding) || "deflate".equalsIgnoreCase(conentEncoding))){
logger.trace("Content-Encoding: {}", conentEncoding);
chain.doFilter(new GZIPRequestWrapper(request), response);
return;
}
chain.doFilter(request, response);
}
}
package cn.devmgr.springcloud;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.zip.DeflaterInputStream;
import java.util.zip.GZIPInputStream;
public class GZIPRequestWrapper extends HttpServletRequestWrapper {
private final static Logger logger = LoggerFactory.getLogger(GZIPRequestWrapper.class);
protected HttpServletRequest request;
public GZIPRequestWrapper(HttpServletRequest request){
super(request);
this.request = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
ServletInputStream sis = request.getInputStream();
InputStream is = null;
String conentEncoding = request.getHeader("Content-Encoding");
if("gzip".equalsIgnoreCase(conentEncoding)){
is = new GZIPInputStream(sis);
}else if("deflate".equalsIgnoreCase(conentEncoding)){
is = new DeflaterInputStream(sis);
}else{
throw new UnsupportedEncodingException(conentEncoding + " is not supported.");
}
final InputStream compressInputStream = is;
return new ServletInputStream(){
ReadListener readListener;
@Override
public int read() throws IOException {
int b = compressInputStream.read();
if(b == -1 && readListener != null) {
readListener.onAllDataRead();
}
return b;
}
@Override
public boolean isFinished(){
try {
return compressInputStream.available() == 0;
} catch (IOException e) {
logger.error("error", e);
if(readListener != null) {
readListener.onError(e);
}
return false;
}
}
@Override
public boolean isReady() {
try {
return compressInputStream.available() > 0;
} catch (IOException e) {
logger.error("error", e);
if(readListener != null) {
readListener.onError(e);
}
return false;
}
}
@Override
public void setReadListener(final ReadListener readListener) {
this.readListener = readListener;
sis.setReadListener(new ReadListener() {
@Override
public void onDataAvailable() throws IOException {
logger.trace("onDataAvailable");
if(readListener != null){
readListener.onDataAvailable();
}
}
@Override
public void onAllDataRead() throws IOException {
logger.trace("onAllDataRead");
}
@Override
public void onError(Throwable throwable) {
logger.error("onError", throwable);
if(readListener != null){
readListener.onError(throwable);
}
}
});
}
};
}
}
CURL的测试命令:
echo '{"type": "json", "length": 2222}' | gzip > body.gz
curl -v -i http://127.0.0.1:8080/ss -H 'Content-Encoding: gzip' -H 'Content-Type:application/json' --data-binary @body.gz