Tomcat 配置gzip无效

来源:互联网 发布:淘宝怎么买弹簧刀 编辑:程序博客网 时间:2024/06/08 14:49

gzip的详细配置请点击笔记:《tomcat的gzip压缩配置及优化效果对比


提升Tomcat性能方法有很多种,使用NIO Connector和启用gzip压缩是其中两种。

NIO:Java New IO,使用了多路复用的技术,无疑要比普通的IO socket要高效。

gzip:对需要传输到前台的内容首先在内存中进行gzip压缩,这样可以大大的减少网络带宽占用。前提是前台的Accept-Encoding允许gzip。

但是,当同时配置了这两个时,会发现大于48KB的文件并没有进行压缩。

经查Tomcat源码,发现org.apache.catalina.servlets.DefaultServlet中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
     * Check if sendfile can be used.
     */ 
    protectedboolean checkSendfile(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  CacheEntry entry, 
                                  longlength, Range range) { 
        if((sendfileSize > 0
            && (entry.resource != null
            && ((length > sendfileSize) || (entry.resource.getContent() == null)) 
            && (entry.attributes.getCanonicalPath() != null
            && (Boolean.TRUE == request.getAttribute("org.apache.tomcat.sendfile.support")) 
            && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade")) 
            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) { 
            request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath()); 
            if(range == null) { 
                request.setAttribute("org.apache.tomcat.sendfile.start",newLong(0L)); 
                request.setAttribute("org.apache.tomcat.sendfile.end",newLong(length)); 
            }else
                request.setAttribute("org.apache.tomcat.sendfile.start",newLong(range.start)); 
                request.setAttribute("org.apache.tomcat.sendfile.end",newLong(range.end + 1)); 
            
            returntrue
        }else
            returnfalse
        
    }
此处的sendfileSize = 48*1024,默认值为48KB,可以发现,当文件大小大于48KB时,Tomcat并未马上将内容写回到output中,而是把文件的路径记录下来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
     * Serve the specified resource, optionally including the data content.
     *
     * @param request The servlet request we are processing
     * @param response The servlet response we are creating
     * @param content Should the content be included?
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet-specified error occurs
     */ 
    protectedvoid serveResource(HttpServletRequest request, 
                                 HttpServletResponse response, 
                                 booleancontent) 
        throwsIOException, ServletException { 
   
            ...... 
   
            // Copy the input stream to our output stream (if requested) 
            if(content) { 
                try
                    response.setBufferSize(output); 
                }catch(IllegalStateException e) { 
                    // Silent catch 
                
                if(ostream != null) { 
                    if(!checkSendfile(request, response, cacheEntry, contentLength, null)) 
                        copy(cacheEntry, renderResult, ostream); 
                }else
                    copy(cacheEntry, renderResult, writer); 
                
            
               
            ...... 
   
    }
并在Http11Processor的process方法的最后一部分,把文件内容以FileChannel的形式写回到前台,不需要先把文件内容先读到用户内存->压缩->写回socket内核内存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
     * Process pipelined HTTP requests using the specified input and output
     * streams.
     *
     * @throws IOException error during an I/O operation
     */ 
    publicSocketState process(NioChannel socket) 
        throwsIOException { 
           
         ...... 
            // Do sendfile as needed: add socket to sendfile and end 
            if(sendfileData != null&& !error) { 
                KeyAttachment ka = (KeyAttachment)socket.getAttachment(false); 
                ka.setSendfileData(sendfileData); 
                sendfileData.keepAlive = keepAlive; 
                SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); 
                //do the first write on this thread, might as well 
                openSocket = socket.getPoller().processSendfile(key,ka,true,true); 
                break
            
        ...... 
   
    }

这种NIO底层读写channel的形式避免了读取到用户内存的开销,也可以提升性能。

目前,尚不清楚使用NIO快,还是gzip较快,有待测试。

如果在使用NIO的同时还一定要用gzip,可以关闭NIO Connector的useSendFile选项。

1
2
3
4
5
6
7
8
<Connector port="8080"protocol="org.apache.coyote.http11.Http11NioProtocol" 
               connectionTimeout="20000"  
               redirectPort="8443" 
                useSendfile="false" 
                compression="on"  
                compressionMinSize="2048"  
                noCompressionUserAgents="gozilla, traviata"  
                compressableMimeType="text/html,text/xml,text/javascript"/>

参考地址:http://tomcat.apache.org/tomcat-6.0-doc/config/http.html
0 0