Tomcat 配备gzip无效
Tomcat 配置gzip无效
此处的sendfileSize = 48*1024,默认值为48KB,可以发现,当文件大小大于48KB时,Tomcat并未马上将内容写回到output中,而是把文件的路径记录下来。
并在Http11Processor的process方法的最后一部分,把文件内容以FileChannel的形式写回到前台,不需要先把文件内容先读到用户内存->压缩->写回socket内核内存。
这种NIO底层读写channel的形式避免了读取到用户内存的开销,也可以提升性能。
提升Tomcat性能方法有很多种,使用NIO Connector和启用gzip压缩是其中两种。
NIO:Java New IO,使用了多路复用的技术,无疑要比普通的IO socket要高效。
gzip:对需要传输到前台的内容首先在内存中进行gzip压缩,这样可以大大的减少网络带宽占用。前提是前台的Accept-Encoding允许gzip。
但是,当同时配置了这两个时,会发现大于48KB的文件并没有进行压缩。
经查Tomcat源码,发现org.apache.catalina.servlets.DefaultServlet中:
/** * 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中,而是把文件的路径记录下来。
/** * 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内核内存。
/** * 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选项。
<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