Tomcat 配置gzip无效

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.
      */ 
     protected boolean checkSendfile(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   CacheEntry entry, 
                                   long length, 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" , new Long(0L)); 
                 request.setAttribute( "org.apache.tomcat.sendfile.end" , new Long(length)); 
             } else
                 request.setAttribute( "org.apache.tomcat.sendfile.start" , new Long(range.start)); 
                 request.setAttribute( "org.apache.tomcat.sendfile.end" , new Long(range.end + 1 )); 
            
             return true
         } else
             return false
        
     }
此处的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
      */ 
     protected void serveResource(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  boolean content) 
         throws IOException, 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
      */ 
     public SocketState process(NioChannel socket) 
         throws IOException { 
           
          ...... 
             // 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
"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

你可能感兴趣的:(Tomcat)