Tomcat 对静态资源的处理
Tomcat 中的请求都是由 Servlet 处理,静态资源也不例外。在默认的 web.xml 中,配置了一个 DefaultServlet 用于处理静态资源,它支持缓存和断点续传。
DefaultServlet 的基本处理过程如下:
- 查找资源是否存在缓存
- 检查是否满足可选 If 头域指定的条件
- 设置响应头域,如 Content-Type、Content-Length、ETag、Last-Modified
- 检查是否满足 Sendfile 的条件,否则将内容拷贝到输出流中
接下来主要分析资源缓存的设计和实现,以及 If 头域的处理。
1. 资源缓存的设计
访问磁盘的速度远远低于访问内存的速度,所以适当的缓存一部分静态资源能够让系统快速响应。
Tomcat 在 6.0.53 版本实现静态资源的处理时,借助了 JNDI 的一些 API(但在使用时感觉与 JNDI 的关系不大),相关类图及核心方法和属性如下:

缓存相关的类:
- ResourceCache: 缓存实现,提供了资源查找、加载、销毁的功能
- CacheEntry: 一个缓存条目,包含缓存名称,如 /tomcat.gif,资源和资源的属性以及对应的目录
资源目录相关的类是:
- EmptyDirContext: 主要用于嵌入式模式,行为就像没有可用资源一样
- FileDirContext: 基于文件系统的资源目录服务
- WARDirContext: 基于 war 文件的目录服务
- Resource: 封装了资源内容,主要有字节数据和输入流
- ResourceAttributes: 资源属性,主要有内容长度和最后修改时间
- ProxyDirContext: 资源缓存和目录服务的代理,提供查找资源缓存、校验缓存是否过期等功能
默认情况下,缓存最大为 10 MB,单个缓存资源最大为 512 KB,缓存的 TTL 为 5s。
一般的,在 Mapper 映射到处理静态资源的 Wrapper 时,会引起资源的加载,基本的方法调用情况如下:
Mapper.map(MessageBytes, MessageBytes, MappingData)
└─Mapper.internalMap(CharChunk, CharChunk, MappingData)
└─Mapper.internalMapWrapper(Mapper$Context, CharChunk, MappingData)
└─ProxyDirContext.lookup(String)
└─ProxyDirContext.cacheLookup(String)
└─ResourceCache.lookup(String)
└─ResourceCache.find(CacheEntry[], String)
缓存资源插入内部数组时是有序的,find 方法就是通过资源名二分查找缓存,资源名就是请求路径,此时有两种情况,缓存命中和未命中。
缓存未命中,在 cacheLookup 方法中会新建一个 CacheEntry 对象,调用 cacheLoad 方法加入到 ResourceCache 的缓存数组中,加入前会对缓存条目进行以下操作:
- 获取并初始化缓存资源属性,主要是文件的 contentLength 和 lastModified
- 如果文件长度小于 512KB,那么将文件内容加载到内存中
- 标记缓存存在,设置缓存时间戳
缓存命中,会对缓存条目进行校验:
- 检查是否过期,当前时间大于缓存条目设置的时间戳
- 如果过期,再检查资源内容是否修改
- 如果修改,清除这个缓存,读取最新内容
以上就是资源缓存简单的处理过程。本文首发于微信公众号:顿悟源码,交流QQ群:673986158
2. If 头域的处理
客户端接收并缓存请求的资源,,当再次请求此资源时,服务端根据特定的请求头域来验证资源是否修改,没有变动,则只返回一个 304 Not Modified 响应,否则返回资源的内容,从而节省带宽。
用于资源验证的头域有两种,分别是:Last-Modified+If-Modified-Since 和 ETag+If-None-Match。
Last-Modified+If-Modified-Since,单位是秒,这个容易理解,如果服务端资源的最后修改时间小于 If-Modified-Since 的值,表示资源无变动。与 If-Modified-Since 对应的有个 If-Unmodified-Since,它类似一个断言,小于此时间戳的资源才返回,大于等于的话会返回 412 Precondition Failed 的错误。
使用时间戳校验有几个弊端:
- 文件有可能只改变修改时间,内容不变
- 文件在秒以下的时间修改无法判断
- 服务器可能不能精确获取文件的最后修改时间。
因此,HTTP 引入了 ETag。ETag(Entity Tags) 资源唯一标识,可看做服务端为资源生成的一个 Token,用于校验资源是否修改。HTTP 只规定 ETag 要放在双引号内,没有规定内容是什么或者要怎么实现,Tomcat 生成 ETag 的逻辑是 "W/\"" + contentLength + "-" + lastModified + "\"",其中 'W/' 表示大小写敏感。
ETag+If-None-Match,If-None-Match 的值由一个或多个 ETag 组成,多个以逗号分割,如果服务端资源的 ETag 与其中的任何一个都不匹配,表示请求的资源有修改;否则无变动。它还有一个特殊值-星号(*),只在资源上传时使用,通常是 PUT 方法,检查是否已经上传过。
此外 If-None-Match 的优先级高于 If-Modified-Since,也就是说,存在 If-None-Match 就不对最后修改时间进行校验。与 If-None-Match 相对的有个 If-Match,它也类似断言,只有资源的 ETag 匹配时才认为没有修改,通常用于断点续传。
Tomcat 实现此部分的核心代码如下:
// 返回 true 是才认为资源有变动
protected boolean checkIfHeaders(HttpServletRequest request,
HttpServletResponse response,ResourceAttributes resourceAttributes)
throws IOException {
return checkIfMatch(request, response, resourceAttributes)
&& checkIfModifiedSince(request, response, resourceAttributes)
&& checkIfNoneMatch(request, response, resourceAttributes)
&& checkIfUnmodifiedSince(request, response, resourceAttributes);
}
2.1 一次请求流程
以请求 /main.css 静态资源为例,第一次请求响应头信息如下:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"72259-1557127244000"
Last-Modified: Mon, 06 May 2019 07:20:44 GMT
Content-Type: text/css
Content-Length: 72259
Date: Mon, 06 May 2019 07:20:57 GMT
第二次请求时,首先看一下请求头域关键信息:
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost:8080
If-Modified-Since:Mon, 06 May 2019 07:20:44 GMT
If-None-Match:W/"72259-1557127244000"
服务器收到请求后就会比对 ETag,这里匹配成功,表示资源没有修改,响应如下:
HTTP/1.1 304 Not Modified
Server: Apache-Coyote/1.1
ETag: W/"72259-1557127244000"
Date: Mon, 06 May 2019 07:21:46 GMT
注意:在复现时,要使用文本类型,如果使用 Chrome 浏览器,记得开启缓存。
2.2 Accept-Ranges
在上文的响应中,服务器设置了一个 Accept-Ranges: bytes 头,字面理解就是可以请求资源的一部分字节,客户端发现有这个头时,就可以尝试断点续传。
解析过程就是对 HTTP 规范的实现,这里不在具体分析了,规范详细信息可查看 RFC7233#section-2.3.
3. SendFile 的处理
检查是否支持 SendFile,NIO 模式下支持此操作,也就是零拷贝,此操作会减少一次到应用内存的拷贝,直接从内核将数据写入通道。Tomcat 在文件大小大于 48KB 时会尝试使用此方式发送。
4. 小结
Tomcat 对静态资源处理的实现还是比较完善的,但还是略逊色于 Nginx 这类 Web 服务器,因为它们能直接处理静态资源,而 Tomcat 还要多做一次映射。一般的都会进行动静分离,让 Tomcat 专注处理动态请求。
Tomcat 对静态资源的处理的更多相关文章
- tomcat缓存静态资源深入
之前看过apach及nginx对于静态资源(含js,图片,css等)部分的缓存,用于加速并减轻后台实际web服务器的压力. 静态资源缓存是WEB服务器优化的一种手段,基本原理如下: 1.客户端浏览器请 ...
- Nginx+Tomcat动静态资源分离
1 创建用户.用户组 useradd -g users www passwd www //设置密码,否则该用户不可用 groupadd -g 888 www //创建用户组 gpasswd -a ww ...
- 【问题管理】-- Tomcat8部署项目加载静态资源html页面编码错误
1.问题背景及解决方式 最近在回顾Tomcat部署Web项目,自己简单地从Tomcat的下载安装及配置server.xml文件入手,学习Tomcat的项目部署,在自己使用IDEA创建了一个简单地web ...
- SpringMVC-开启静态资源访问权限
1.配置 <mvc:resources mapping="/js/**" location="/js/"/> mapping:代表js目录下的所有文 ...
- WebPack系列:Webpack编译的代码如何在tomcat中使用时静态资源路径不对的问题如何解决
问题: 使用webpack+vue做前端,使用tomcat提供api,然后npm run build之后需要将编译,生成如下文件: | index.html \---appserver ...
- 通过配置tomcat虚拟路径配置站点的静态资源
我们常常站点中会提供给用户上传文件.图片.视频或者诸如为了提高性能生成的静态文件等存储在站点应用中.但如果静态资源文件和项目文件在同一个目录下,当我们重新部署文件时,war重新解压会导致静态资源文件的 ...
- 解决Tomcat无法加载css和js等静态资源文件
解决思路有两个 一是,你使用了Apache服务器,html不交给Tomcat处理,所以你找不到Html等静态资源,所以你先停掉阿帕奇,然后只用Tomcat猫试试. 二是,像我一样,使用了Jetty开发 ...
- 关于linux下部署JavaWeb项目,nginx负责静态资源访问,tomcat负责处理动态请求的nginx配置
1.项目的运行环境 linux版本 [root@localhost ~]# cat /proc/version Linux version -.el6.x86_64 (mockbuild@x86-.b ...
- tomcat配置外部静态资源映射路径
一.背景 1.有一个录音软件每天生成很多新的录音文件. 2.现在想通过一个WEB项目页面下载这些录音文件. 3.很显然这些录音文件放在WEB项目下不是很合适(WEB项目更新是个大麻烦,海量的录音文件要 ...
随机推荐
- Linux shell 函数应用示例01
函数Function的使用 定义函数 (1) 函数名称() { ... ... } (2) function 函数名称{ ... ... } 调用函数 ...
- Linux 批量查找并替换文件夹下所有文件的内容
1.批量查找某个目下文件的包含的内容 cd etc grep -rn "查找的内容" ./ 2.批量替换某个目下所有包含的文件的内容 cd etc sed -i "s/查 ...
- 解决nginx反向代理webservice的soap:address location问题
原文:https://blog.csdn.net/mn960mn/article/details/50716768 一:首先来发布一个web service package com.ws.servic ...
- BSGS算法(大小步算法)
$BSGS$ 算法 $Baby\ Steps\ Giant\ Steps$. 致力于解决给定两个互质的数 $a,\ p$ 求一个最小的非负整数 $x$ 使得 $a^x\equiv b(mod\ p)$ ...
- Win10下默认显示文件的扩展名
默认不显示文件的后缀名 1.右击此电脑,点击属性 2.点击控制面板主页 3.点击文件资源管理器选项 4.切换到查看选项卡,取消勾选“隐藏已知文件类型的扩展名”,应用->确定 结果:
- MySQL 学习网站
[大型网站] CSDN论坛MySQL版块 https://bbs.csdn.net/forums/MySQL MySlide搜索MySQL https://myslide.cn/slides/1758 ...
- KDiff3使用指南
http://kdiff3.sourceforge.net/ KDiff3 is a diff and merge program that compares or merges two or thr ...
- 洛谷 P2253 好一个一中腰鼓! 题解
P2253 好一个一中腰鼓! 题目背景 话说我大一中的运动会就要来了,据本班同学剧透(其实早就知道了),我萌萌的初二年将要表演腰鼓[喷],这个无厘头的题目便由此而来. Ivan乱入:"忽一人 ...
- Koa帮我们做了什么
整理web渲染思路,与KOA作比较 1.开启服务器并监听端口,注册监听事件 // 原生 let http = require('http') const server = http.createSer ...
- ZROI 暑期高端峰会 A班 Day1 序列数据结构
FBI Warning:本文包含大量人类的本质之一 CF643G 维护一个序列,可以区间赋值,求区间中出现超过 \(p\%\) 的数. 允许输出不对的数,允许重复输出,但是所有对的数都一定要输出.而且 ...