JSP 页面优化里,缓存通常是最先该做、也最容易看到效果的一步。对部署在 Debian 上的 Web 应用来说,很多性能问题并不需要先重构业务逻辑,先把页面片段、静态资源、浏览器访问链路和反向代理这一层理顺,往往就能把响应时间和后端压力一起压下来。
这篇文章按“缓存放在哪一层、解决什么问题”的思路来整理常用方案。你可以据此判断:当前瓶颈究竟是 JSP 动态渲染、静态资源分发、浏览器重复请求,还是高并发下的后端承压,再决定是单点优化还是多层组合。
先判断:JSP 缓存应该放在哪一层
JSP 页面缓存并不是一种手段,而是一组分层策略。简单说:
- 如果页面里有重复出现、更新不频繁的内容,优先考虑容器内或页面片段缓存。
- 如果页面生成逻辑本身代价高,可以把结果或中间数据交给第三方缓存库。
- 如果慢的是图片、CSS、JavaScript 等静态资源,CDN 更直接。
- 如果用户频繁重复访问同一页面,浏览器缓存能立刻减少回源请求。
- 如果业务已经进入高并发阶段,反向代理缓存通常是生产环境里收益最高的一层。
下面分别展开。
使用 Servlet 容器的内置缓存
很多团队一开始就去找外部组件,反而忽略了 Servlet 容器本身已经提供的基础能力。对于结构相对稳定的 JSP 页面,这一层通常最容易落地。
Tomcat 中的页面片段缓存
如果页面里有页头、侧栏、公告区这类更新不频繁的内容,可以通过 <jsp:include> 或 <c:import> 复用片段,并配合缓存时间减少重复渲染。
<jsp:include page="header.jsp" flush="false"><jsp:param name="cacheTime" value="3600"/></jsp:include>这类方式适合“页面整体动态,但局部内容相对稳定”的场景。它不能替代完整页面缓存,但能先削掉一部分重复计算。
在 Servlet 层设置缓存头
如果希望浏览器或中间代理节点缓存响应结果,可以直接在 Servlet 层返回缓存头:
response.setHeader("Cache-Control", "max-age=3600");这一做法的优点是简单、成本低,特别适合短时间内内容不会变化的页面或接口响应。不过前提是你要确认内容在缓存周期内可以被复用,否则用户可能看到旧数据。
内置能力不够时,用 Ehcache 做应用层缓存
当页面逻辑更复杂,或者你希望对缓存对象、容量、失效时间做更细的控制时,第三方缓存库会更灵活。原文里提到的 Ehcache,就是 Java 生态中较常见的一种选择。
1. 添加 Ehcache 依赖
先在项目中加入 Maven 依赖:
<dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.10.6</version></dependency>2. 配置 ehcache.xml
接着创建 ehcache.xml,定义缓存名称、容量和过期策略。下面这段配置表示:
- 缓存区名称为
jspCache - 内存中最多保存
1000个元素 timeToIdleSeconds和timeToLiveSeconds都是3600overflowToDisk="false",即不写入磁盘
<ehcache><cache name="jspCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="false"/></ehcache>3. 在 JSP 中读取和写入缓存
页面逻辑上通常是“先查缓存,未命中再生成内容,然后写回缓存”。原始示例如下:
<%@ page import="net.sf.ehcache.CacheManager" %><%@ page import="net.sf.ehcache.Element" %><%CacheManager cacheManager = CacheManager.newInstance();Element cachedElement = cacheManager.get("jspCacheKey");if (cachedElement == null) {// 生成内容String content = "Cached Content";cachedElement = new Element("jspCacheKey", content);cacheManager.add(cachedElement);}out.print(cachedElement.getObjectValue());%>这类应用层缓存更适合“生成成本高、命中后收益明显”的内容,例如热门页面数据、聚合结果或重复计算较多的模块。
静态资源多时,把图片、CSS、JavaScript 交给 CDN
如果页面首屏慢主要来自图片、CSS、JavaScript 文件下载,单靠 JSP 本身的缓存并不能彻底解决问题。这时更有效的办法是把静态资源交给 CDN。
CDN 的价值在于“就近访问”:资源会被缓存到各地边缘节点,用户请求时优先从距离更近的节点获取,延迟通常会比直接回源更低。
CDN 的基本配置步骤
- 选择 CDN 提供商:Cloudflare、Akamai 等都属于成熟方案。
- 配置 DNS:把域名的 DNS 记录指向 CDN 服务商的服务器。
- 上传静态资源:将图片、CSS、JavaScript 等资源上传到 CDN 平台,由平台负责分发。
这类方案尤其适合用户地域分布较广、静态资源占比高的站点。它提升的不只是访问速度,也能明显减少源站带宽压力。
不要忽略浏览器缓存
浏览器缓存是最常见、也最容易被漏掉的一层。它的思路很直接:告诉浏览器某份内容在一段时间内可以直接复用,这样用户再次访问时就不必重新请求服务器。
通过 HTTP 头控制缓存策略
在 Servlet 或 JSP 中,可以这样设置:
response.setHeader("Cache-Control", "public, max-age=3600");response.setHeader("Expires", "Wed, 21 Oct 2025 07:28:00 GMT");其中:
Cache-Control用来定义缓存方式和有效时长Expires用来指定一个明确的过期时间
如果页面或资源更新频率低,这一层可以非常高效;但如果内容经常变化,就要谨慎设置过期时间,避免缓存命中率上去了,内容一致性却变差。
生产环境常用方案:Nginx 反向代理缓存
当应用进入生产环境,尤其是需要承接更高并发时,反向代理缓存通常是最值得优先考虑的一层。它部署在 JSP 页面和后端服务器之间,可以直接替后端挡住一部分重复请求。
Nginx 配置示例
server {listen 80;server_name example.com;location / {proxy_pass http://localhost:8080;proxy_cache my_cache;proxy_cache_valid 200 302 10m;proxy_cache_valid 404 1m;}proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;}从这段配置可以看出几个关键点:
- 请求先由 Nginx 转发到
http://localhost:8080 - 启用缓存区
my_cache - 对
200、302响应缓存10m - 对
404响应缓存1m - 缓存路径位于
/var/cache/nginx,最大体积1g
这一层的优势是覆盖范围广,既能缓存动态页面,也能减轻后端 Tomcat 或其他 Java 服务的压力。对于访问量较大的站点,它往往比单纯在 JSP 内部做优化更见效。
该怎么选:5 种方案的适用场景
把这几种缓存方式放在一起看,选择会更清楚:
- 容器内置缓存:适合快速上手,先优化 JSP 页面片段和基础缓存头。
- Ehcache:适合对缓存对象、容量、失效时间有更细控制需求的应用层场景。
- CDN:适合静态资源多、用户分布广的站点。
- 浏览器缓存:适合减少重复访问带来的回源请求。
- Nginx 反向代理缓存:适合生产环境、高并发、后端压力大的业务。
实际落地时,这些方案并不是互斥关系。更常见的做法是组合使用:静态资源走 CDN,浏览器缓存兜底,热点页面由 Nginx 缓存,应用内部再用 Ehcache 缓住高成本计算结果。
对 Debian 上的 JSP 应用来说,真正关键的不是“有没有上缓存”,而是先看清瓶颈在哪一层,再把缓存放到最合适的位置。这样做,性能提升通常比盲目调参数更直接,也更稳定。












