tomcat和jetty對靜態資源的處理和客戶端緩存的處理

tomcat和jetty對靜態資源的處理和客戶端緩存的處理


原文鏈接:http://www.javaarch.net/jiagoushi/867.htm


這兩個默認servlet名稱都是defaultservlet,然後在web.xml中就可以添加下面的配置讓應用支持都靜態資源的處理,對應的這些靜態資源的目錄則是在webapp根目錄下,這裏其實可以不用配置servlet名稱,對於名稱爲default的url,tomcat和jetty都會作爲靜態資源文件處理

	<servlet-mapping>
     	<servlet-name>default</servlet-name>
    	 <url-pattern>*.css</url-pattern>
	</servlet-mapping>
	 
	<servlet-mapping>
	    <servlet-name>default</servlet-name>
	    <url-pattern>*.gif</url-pattern>
	 </servlet-mapping>
	    
	 <servlet-mapping>
	     <servlet-name>default</servlet-name>
	     <url-pattern>*.jpg</url-pattern>
	 </servlet-mapping>
	    
	 <servlet-mapping>
	     <servlet-name>default</servlet-name>
	     <url-pattern>*.js</url-pattern>
	 </servlet-mapping>
	 
	 <servlet-mapping>
	     <servlet-name>default</servlet-name>
	     <url-pattern>*.swf</url-pattern>
	 </servlet-mapping>


那麼我們來看看tomcat和jetty對靜態資源的客戶端緩存的處理邏輯:


tomcat,tomcat在default的servlet支持一些參數,如果有需要那麼就需要配置servlet了,

	<servlet>  
	  <servlet-name>default</servlet-name>  
	  <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>  
	  <init-param>  
		  <param-name>debug</param-name>  
		  <param-value>0</param-value>  
	  </init-param>  
	  <init-param>  
		  <param-name>listings</param-name>  
		  <param-value>false</param-value>  
	  </init-param>  
	  <load-on-startup>1</load-on-startup>  
	</servlet> 

在對於靜態資源處理的客戶端緩存的代碼如下

	// ETag header
    response.setHeader("ETag", cacheEntry.attributes.getETag());

    // Last-Modified header
    response.setHeader("Last-Modified", cacheEntry.attributes.getLastModifiedHttp());

這裏的etag計算規則如下:

	long contentLength = getContentLength();
	long lastModified = getLastModified();
	if ((contentLength >= 0) || (lastModified >= 0)) {
		weakETag = "W/\"" + contentLength + "-" +
				   lastModified + "\"";
	}
	


輸出的reponse header如下:

	HTTP/1.1 200 OK
	Server: Apache-Coyote/1.1
	Accept-Ranges: bytes
	ETag: W/"7482-1371188756000"
	Last-Modified: Fri, 14 Jun 2013 05:45:56 GMT
	Content-Type: text/css;charset=GBK
	Content-Length: 7482
	Date: Sun, 16 Jun 2013 07:05:37 GMT

第二次請求時,會先對reqeust的etag和last-modified進行比對,如果沒更新的話則返回304

protected ArrayList<Range> parseRange(HttpServletRequest request,
            HttpServletResponse response,
            ResourceAttributes resourceAttributes) throws IOException {

        // Checking If-Range
        String headerValue = request.getHeader("If-Range");

        if (headerValue != null) {

            long headerValueTime = (-1L);
            try {
                headerValueTime = request.getDateHeader("If-Range");
            } catch (IllegalArgumentException e) {
                // Ignore
            }

            String eTag = resourceAttributes.getETag();
            long lastModified = resourceAttributes.getLastModified();

            if (headerValueTime == (-1L)) {

                // If the ETag the client gave does not match the entity
                // etag, then the entire entity is returned.
                if (!eTag.equals(headerValue.trim()))
                    return FULL;

            } else {

                // If the timestamp of the entity the client got is older than
                // the last modification date of the entity, the entire entity
                // is returned.
                if (lastModified > (headerValueTime + 1000))
                    return FULL;

            }

        }
	

第二次返回:

	HTTP/1.1 304 Not Modified
	Server: Apache-Coyote/1.1
	ETag: W/"2640-1371187966000"
	Date: Sun, 16 Jun 2013 07:23:27 GMT

那麼這裏對靜態資源瀏覽器就自動能夠緩存起來了。當然tomcat和jetty服務器也會對靜態資源進行緩存。


jetty對這個處理也差不多,不過jetty對於靜態資源的緩存策略可以做更多參數設置,這些參數都是在web.xml配置servlet的時候可以進行設置的。

_cacheControl=getInitParameter("cacheControl");

        String resourceCache = getInitParameter("resourceCache");
        int max_cache_size=getInitInt("maxCacheSize", -2);
        int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
        int max_cached_files=getInitInt("maxCachedFiles", -2);
        if (resourceCache!=null)
        {
            if (max_cache_size!=-1 || max_cached_file_size!= -2 || max_cached_files!=-2)
                LOG.debug("ignoring resource cache configuration, using resourceCache attribute");
            if (_relativeResourceBase!=null || _resourceBase!=null)
                throw new UnavailableException("resourceCache specified with resource bases");
            _cache=(ResourceCache)_servletContext.getAttribute(resourceCache);

            LOG.debug("Cache {}={}",resourceCache,_cache);
        }

        _etags = getInitBoolean("etags",_etags);


處理代碼:


寫header

    /* ------------------------------------------------------------ */
    protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
    throws IOException
    {        
        if (content.getContentType()!=null && response.getContentType()==null)
            response.setContentType(content.getContentType().toString());

        if (response instanceof Response)
        {
            Response r=(Response)response;
            HttpFields fields = r.getHttpFields();

            if (content.getLastModified()!=null)
                fields.put(HttpHeader.LAST_MODIFIED,content.getLastModified());
            else if (content.getResource()!=null)
            {
                long lml=content.getResource().lastModified();
                if (lml!=-1)
                    fields.putDateField(HttpHeader.LAST_MODIFIED,lml);
            }

            if (count != -1)
                r.setLongContentLength(count);

            writeOptionHeaders(fields);
            
            if (_etags)
                fields.put(HttpHeader.ETAG,content.getETag());
        }
        else
        {
            long lml=content.getResource().lastModified();
            if (lml>=0)
                response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml);

            if (count != -1)
            {
                if (count<Integer.MAX_VALUE)
                    response.setContentLength((int)count);
                else
                    response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(count));
            }

            writeOptionHeaders(response);

            if (_etags)
                response.setHeader(HttpHeader.ETAG.asString(),content.getETag().toString());
        }
    }

判斷靜態資源是否修改過

 /* ------------------------------------------------------------ */
    /* Check modification date headers.
     */
    protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, Resource resource, HttpContent content)
    throws IOException
    {
        try
        {
            if (!HttpMethod.HEAD.is(request.getMethod()))
            {
                if (_etags)
                {
                    String ifm=request.getHeader(HttpHeader.IF_MATCH.asString());
                    if (ifm!=null)
                    {
                        boolean match=false;
                        if (content!=null && content.getETag()!=null)
                        {
                            QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifm,", ",false,true);
                            while (!match && quoted.hasMoreTokens())
                            {
                                String tag = quoted.nextToken();
                                if (content.getETag().toString().equals(tag))
                                    match=true;
                            }
                        }

                        if (!match)
                        {
                            Response r = Response.getResponse(response);
                            r.reset(true);
                            r.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
                            return false;
                        }
                    }
                    
                    String ifnm=request.getHeader(HttpHeader.IF_NONE_MATCH.asString());
                    if (ifnm!=null && content!=null && content.getETag()!=null)
                    {
                        // Look for GzipFiltered version of etag
                        if (content.getETag().toString().equals(request.getAttribute("o.e.j.s.GzipFilter.ETag")))
                        {
                            Response r = Response.getResponse(response);
                            r.reset(true);
                            r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                            r.getHttpFields().put(HttpHeader.ETAG,ifnm);
                            return false;
                        }
                        
                        
                        // Handle special case of exact match.
                        if (content.getETag().toString().equals(ifnm))
                        {
                            Response r = Response.getResponse(response);
                            r.reset(true);
                            r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                            r.getHttpFields().put(HttpHeader.ETAG,content.getETag());
                            return false;
                        }

                        // Handle list of tags
                        QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifnm,", ",false,true);
                        while (quoted.hasMoreTokens())
                        {
                            String tag = quoted.nextToken();
                            if (content.getETag().toString().equals(tag))
                            {
                                Response r = Response.getResponse(response);
                                r.reset(true);
                                r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                                r.getHttpFields().put(HttpHeader.ETAG,content.getETag());
                                return false;
                            }
                        }
                        
                        // If etag requires content to be served, then do not check if-modified-since
                        return true;
                    }
                }
                
                // Handle if modified since
                String ifms=request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
                if (ifms!=null)
                {
                    //Get jetty's Response impl
                    Response r = Response.getResponse(response);
                                       
                    if (content!=null)
                    {
                        String mdlm=content.getLastModified();
                        if (mdlm!=null)
                        {
                            if (ifms.equals(mdlm))
                            {
                                r.reset(true);
                                r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                                r.flushBuffer();
                                return false;
                            }
                        }
                    }

                    long ifmsl=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
                    if (ifmsl!=-1)
                    {
                        if (resource.lastModified()/1000 <= ifmsl/1000)
                        { 
                            r.reset(true);
                            r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                            r.flushBuffer();
                            return false;
                        }
                    }
                }

                // Parse the if[un]modified dates and compare to resource
                long date=request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString());

                if (date!=-1)
                {
                    if (resource.lastModified()/1000 > date/1000)
                    {
                        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
                        return false;
                    }
                }

            }
        }
        catch(IllegalArgumentException iae)
        {
            if(!response.isCommitted())
                response.sendError(400, iae.getMessage());
            throw iae;
        }
        return true;
    }
	

第一次訪問的response header頭:

	HTTP/1.1 200 OK
	Server: Apache-Coyote/1.1
	Accept-Ranges: bytes
	ETag: W/"7482-1371188756000"
	Last-Modified: Fri, 14 Jun 2013 05:45:56 GMT
	Content-Type: text/css;charset=GBK
	Content-Length: 7482
	Date: Sun, 16 Jun 2013 07:05:37 GMT


第二次訪問:

	HTTP/1.1 304 Not Modified
	Server: Jetty(6.1.26)


不過處理靜態資源大型網站肯定不是tomcat或者jetty,基本都是用apache或者nginx等來處理靜態處理,性能更好。這裏只是列出tomcat和jetyy對靜態資源的處理和客戶端緩存的支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章