通過GZIP優化效能
原創文章,轉載請註明
gzip為一種壓縮技術,在網路http傳輸中得到應用。gzip需要web容器,瀏覽器的支援。
看一下效果
壓縮前:
壓縮後:
這裡的時間不需要糾結,開發環境在本機,這裡的效能損耗主要在cpu上,也就是壓縮時消耗的cpu,而且本機還跑了其他東西,不同時刻的環境不太一樣。
1、tomcat配置
tomcat中使用gzip需要進行配置,在server.xml中,在Connector標籤中加入如下屬性
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla,traviata"
compressableMimeType="text/html,text/css.text/javascript"
compression:指定是否開啟壓縮
compressionMinSize:表示小於該值進行壓縮,單位為Byte
noCompressionUserAgents:表示不進行壓縮的瀏覽器
compressableMimeType:表示哪些格式的檔案需要被壓縮
注意:圖片不要進行壓縮,因為圖片完全可以在專案開發中使用壓縮後的圖片。這樣避免了壓縮對於CPU的消耗
2、程式碼
除了進行tomcat配置之外,還需要對請求寫一個filter進行壓縮。
2.1、GZIPOutputStream
主要是通過該類進行壓縮,它由java.util.zip包提供
2.2、ServletResponse、HttpServletResponse、ServletResponseWrapper、HttpServletResponseWrapper、WrappedOutputStream
需要繼承HttpServletResponseWrapper,複寫其中的getWriter,getOutputStream方法從而獲得響應資料,然後就可以呼叫gzipOutputStream進行壓縮。
這裡順道說一下這幾個類的關係
ServletResponse為最基本的介面,表示response。
HttpServletResponse擴充套件了ServletRespones,表示http協議下的response
ServletResponseWrapper實現了ServletResponse
HttpServletResponseWrapper實現了HttpServletResponse
所以我們平時用的HttpServletResponse預設的實現為HttpServletResponseWrapper,struts2似乎在這之上又包裝了一層。
WrappedOutputStream:在我們呼叫getOutputStream方法時,會通過該介面的write方法輸出byte
2.3思路
思路很清晰了,通過繼承HttpServletResponseWrapper重寫getWriter,getOutputStream獲得響應資料,在stream的輸出時,需要通過WrappedOutputStream的write方法輸出byte。最後把輸出的byte通過gzipOutputStream進行壓縮
來看一個實現程式碼
package com.zs.vehicle.filter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.zs.vehicle.utils.Wrapper;
public class GZipFilter implements Filter{
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletResponse resp=(HttpServletResponse) response;
HttpServletRequest req=(HttpServletRequest) request;
if(isGZipEncoding(req)){
Wrapper wrapper=new Wrapper(resp);
chain.doFilter(request, wrapper);
byte[] gzipData=gzip(wrapper.getResponseData());
resp.addHeader("Content-Encoding", "gzip");
resp.setContentLength(gzipData.length);
ServletOutputStream output=response.getOutputStream();
output.write(gzipData);
output.flush();
}else{
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
private static boolean isGZipEncoding(HttpServletRequest request){
boolean flag=false;
String encoding=request.getHeader("Accept-Encoding");
if(encoding.indexOf("gzip")!=-1){
flag=true;
}
return flag;
}
private byte[] gzip(byte[] data){
ByteArrayOutputStream byteOutput=new ByteArrayOutputStream(10240);
GZIPOutputStream output=null;
try{
output=new GZIPOutputStream(byteOutput);
output.write(data);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
output.close();
}catch(Exception e){
e.printStackTrace();
}
}
return byteOutput.toByteArray();
}
}
package com.zs.vehicle.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class Wrapper extends HttpServletResponseWrapper{
public static final int OT_NONE=0;
public static final int OT_WRITER=1;
public static final int OT_STREAM=2;
private int outputType=OT_NONE;
private ServletOutputStream output=null;
private PrintWriter writer=null;
private ByteArrayOutputStream buffer=null;
public Wrapper(HttpServletResponse response) {
super(response);
buffer=new ByteArrayOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException{
if(outputType==OT_STREAM)
throw new IllegalStateException();
else if(outputType==OT_WRITER)
return writer;
else{
outputType=OT_WRITER;
writer=new PrintWriter(new OutputStreamWriter(buffer,getCharacterEncoding()));
return writer;
}
}
@Override
public ServletOutputStream getOutputStream() throws IOException{
if(outputType==OT_WRITER)
throw new IllegalStateException();
else if(outputType==OT_STREAM)
return output;
else{
outputType=OT_STREAM;
output=new WrappedOutputStream(buffer);
return output;
}
}
@Override
public void flushBuffer() throws IOException{
if(outputType==OT_WRITER)
writer.flush();
if(outputType==OT_STREAM)
output.flush();
}
@Override
public void reset(){
outputType=OT_NONE;
buffer.reset();
}
public byte[] getResponseData() throws IOException{
flushBuffer();
return buffer.toByteArray();
}
class WrappedOutputStream extends ServletOutputStream{
private ByteArrayOutputStream buffer;
public WrappedOutputStream(ByteArrayOutputStream buffer){
this.buffer=buffer;
}
@Override
public void write(int b) throws IOException {
buffer.write(b);
}
public byte[] toByteArray(){
return buffer.toByteArray();
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return true;
}
@Override
public void setWriteListener(WriteListener listener) {
// TODO Auto-generated method stub
}
}
}
3、spring-boot
對於spring-boot來說,由於其內嵌了web容器,那麼它的行為是怎樣的呢。
spring-boot的web容器是預設開啟了gzip壓縮的,而且不會對圖片進行壓縮。