从 Tomcat 7 升级到 8 时,我遇到了同样的问题:关于缓存的持续大量日志警告。
1. 简答
Context
在你的 xml 元素中添加这个$CATALINA_BASE/conf/context.xml
:
<!-- The default value is 10240 kbytes, even when not added to context.xml.
So increase it high enough, until the problem disappears, for example set it to
a value 5 times as high: 51200. -->
<Resources cacheMaxSize="51200" />
所以默认是10240
(10 mbyte),所以设置一个比这个大的大小。然后调整到警告消失的最佳设置。请注意,警告可能会在交通繁忙的情况下再次出现。
1.1 原因(简短说明)
该问题是由于缓存条目小于这些条目的 TTL 而导致 Tomcat 无法达到其目标缓存大小。所以 Tomcat 没有足够的缓存条目可以过期,因为它们太新鲜了,所以它无法释放足够的缓存并因此输出警告。
Tomcat 7 中没有出现该问题,因为 Tomcat 7 在这种情况下根本没有输出警告。(导致你和我在没有得到通知的情况下使用糟糕的缓存设置。)
与缓存的大小和 TTL 相比,在相对较短的时间内接收到相对大量的资源 HTTP 请求(通常是静态的)时,就会出现此问题。如果缓存达到其最大值(默认为 10mb)并且超过 95% 的大小具有新的缓存条目(新鲜意味着缓存中少于 5 秒),那么您将收到 Tomcat 尝试的每个 webResource 的警告消息加载到缓存中。
1.2 可选信息
如果您需要在正在运行的服务器上调整 cacheMaxSize 而无需重新启动它,请使用 JMX。
最快的解决方法是完全禁用 cache: <Resources cachingAllowed="false" />
,但这不是最理想的,所以像我刚才描述的那样增加 cacheMaxSize 。
2.长答案
2.1 背景资料
WebSource是 Web 应用程序中的文件或目录。出于性能原因,Tomcat 可以缓存 WebSource。静态资源缓存的最大值(所有资源总计)默认为 10240 kbyte (10 mbyte)。当请求 webResource 时(例如加载静态图像时),将 webResource 加载到缓存中,然后将其称为缓存条目。每个缓存条目都有一个TTL(生存时间),即允许缓存条目留在缓存中的时间。当 TTL 过期时,缓存条目有资格从缓存中删除。cacheTTL 的默认值为 5000 毫秒(5 秒)。
关于缓存还有更多要讲的,但这与问题无关。
2.2 原因
Cache 类中的以下代码详细显示了缓存策略:
152 // 内容不会被缓存,但我们仍然需要元数据大小
153 long delta = cacheEntry. 获取大小();
154 尺寸。addAndGet(增量);
156 if ( size.get () > maxSize) {
157 // 处理资源无序以提高速度。交易缓存
158 // 效率(较旧的条目可能在较旧的
159 // 之前被驱逐)以提高速度,因为这是
160 // 请求处理的关键路径
161 long targetSize =
162 maxSize * (100 - TARGET_FREE_PERCENT_GET) / 100;
163 long newSize = evict (
164 targetSize, resourceCache.values ( ). iterator ()); 165 if (newSize > maxSize) { 166 // 无法为该资源创建足够的空间167 // 将其从缓存中移除168 removeCacheEntry (path); 169 日志。警告(sm.getString(“cache.addFail”,路径));170 } 171 }
加载 webResource 时,代码会计算缓存的新大小。如果计算的大小大于默认的最大大小,则必须删除一个或多个缓存条目,否则新大小将超过最大值。所以代码将计算一个“targetSize”,这是缓存想要保持的大小(作为最佳值),默认为最大值的 95%。为了达到这个 targetSize,必须从缓存中删除/逐出条目。这是使用以下代码完成的:
215 private long evict( long targetSize, Iterator < CachedResource > iter) {
217 long now = System. 当前时间米利斯();
219 长newSize = 大小。得到();
221 while (newSize > targetSize && iter.hasNext ( )) {
222 CachedResource资源 = iter. 下一个();
224 // 不要过期在 TTL
225内检查过的任何内容 if (resource.getNextCheck ( ) > now) {
226 继续;
227 }
229 // 从缓存中移除条目
230 removeCacheEntry (resource.getWebappPath ( ));
第232 章 得到();
233 }
235 返回新尺寸;
第236 章
因此,当缓存条目的 TTL 过期且尚未达到 targetSize 时,将删除缓存条目。
在尝试通过逐出缓存条目来释放缓存之后,代码将执行以下操作:
165 if (newSize > maxSize) {
166 // 无法为该资源创建足够的空间
167 // 将其从缓存中移除
168 removeCacheEntry (path);
169 日志。警告(sm.getString(“cache.addFail”,路径));
170 }
因此,如果尝试释放缓存后,大小仍然超过最大值,则会显示无法释放的警告消息:
cache.addFail=Unable to add the resource at [{0}] to the cache for web application [{1}] because there was insufficient free space available after evicting expired cache entries - consider increasing the maximum size of the cache
2.3 问题
所以正如警告信息所说,问题是
驱逐过期缓存条目后可用空间不足 - 考虑增加缓存的最大大小
如果您的 Web 应用程序在短时间内(5 秒)内加载了大量未缓存的 webResources(大约缓存的最大值,默认为 10mb),那么您将收到警告。
令人困惑的部分是 Tomcat 7 没有显示警告。这仅仅是由这个 Tomcat 7 代码引起的:
1606 // 向缓存添加新条目
1607 synchronized (cache) {
1608 // 检查缓存大小,如果太大则删除元素
1609 if ( (cache.lookup (name) == null ) && cache.allocate ( entry.size ) ) { 1610 缓存。加载(条目);1611 } 1612 }
结合:
231 while (toFree > 0) {
232 if (attempts == maxAllocateIterations) {
233 // 放弃,不改变当前缓存
234 return false ;
第235 章
因此,Tomcat 7 在无法释放缓存时根本不会输出任何警告,而 Tomcat 8 会输出警告。
因此,如果您使用具有与 Tomcat 7 相同的默认缓存配置的 Tomcat 8,并且在 Tomcat 8 中收到警告,那么您(和我的)Tomcat 7 缓存设置在没有警告的情况下表现不佳。
2.4 解决方案
有多种解决方案:
- 增加缓存(推荐)
- 降低 TTL(不推荐)
- 禁止缓存日志警告(不推荐)
- 禁用缓存
2.4.1。增加缓存(推荐)
如此处所述:http: //tomcat.apache.org/tomcat-8.0-doc/config/resources.html
通过<Resources cacheMaxSize="XXXXX" />
在Context
元素中添加$CATALINA_BASE/conf/context.xml
,其中“XXXXX”代表增加的缓存大小,以千字节为单位。默认值为 10240(10 兆字节),因此请设置比此更大的大小。
您必须调整最佳设置。请注意,当您突然增加流量/资源请求时,问题可能会再次出现。
为避免每次要尝试新的缓存大小时都必须重新启动服务器,您可以使用 JMX 更改它而无需重新启动。
要启用 JMX,请将其添加到$CATALINA_BASE/conf/server.xml
元素Server
中:
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="6767" rmiServerPortPlatform="6768" />
并 catalina-jmx-remote.jar
从https://tomcat.apache.org/download-80.cgi下载并将其放入$CATALINA_HOME/lib
. 然后使用 jConsole(默认情况下随 Java JDK 提供)通过 JMX 连接到服务器,并在服务器运行时查看设置以增加缓存大小。这些设置的更改应立即生效。
2.4.2. 降低 TTL(不推荐)
将该cacheTtl
值降低低于 5000 毫秒并调整为最佳设置。
例如:<Resources cacheTtl="2000" />
这实际上归结为在不使用内存的情况下在内存中拥有和填充缓存。
2.4.3。禁止缓存日志警告(不推荐)
配置日志记录以禁用org.apache.catalina.webresources.Cache
.
有关登录 Tomcat 的更多信息:http: //tomcat.apache.org/tomcat-8.0-doc/logging.html
2.4.4。禁用缓存
cachingAllowed
您可以通过设置来禁用缓存false
。
<Resources cachingAllowed="false" />
虽然我记得在 Tomcat 8 的 beta 版本中,我使用 JMX 来禁用缓存。(不知道为什么,但是通过 server.xml 禁用缓存可能会出现问题。)