Tomcat 8出现问题 - org.apache.catalina.webresources.Cache.getResource无法添加资源

142

我刚刚将Tomcat从版本7.0.52升级到8.0.14。

许多静态图像文件显示以下内容:

org.apache.catalina.webresources.Cache.getResource无法将资源[/ base / 1325 / WA6144-150x112.jpg]添加到高速缓存中,因为在淘汰过期的缓存条目后没有足够的可用空间 - 考虑增加缓存的最大大小

我没有指定任何特定的资源设置,并且在7.0.52中没有遇到此问题。

我发现在错误报告中提到了这种情况在启动时发生,但据说已经修复。 对于我来说,当请求资源时,这个问题不是在启动时而是不断地发生。

其他人也有这个问题吗?

尝试至少禁用缓存,但我找不到如何指定不使用缓存的示例。在Tomcat的8版本中,上下文中的属性已经消失了。尝试添加资源,但无法正确配置。

<Resource name="file" 
    cachingAllowed="false"
    className="org.apache.catalina.webresources.FileResourceSet"
/>  

谢谢。


没有回复 - 我猜我一定是唯一遇到这个问题的人。 - iainmac999
2
解决方案在这里:http://serverfault.com/questions/644415/tomcat-8-org-apache-catalina-webresources-cache-getresource - Dmitry
1
关于Tomcat 8上下文中缺失的属性,以下是迁移指南的摘录(重点在我这里):“资源的重构也导致了一些属性从默认的Context实现(org.apache.catalina.core.StandardContext)中被删除。以下属性现在可以通过Web应用程序使用的资源实现进行配置”。更多信息请参见相关的迁移指南 - informatik01
1
@iainmac999,两年来从未选择正确答案,我们可以认为它是双向的吗? - davidjmcclelland
5个回答

242

当我从Tomcat 7升级到8时,遇到了同样的问题:关于缓存的大量日志警告。

1. 简短回答

$CATALINA_BASE/conf/context.xmlContext 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兆字节),因此请将大小设置得比此更高。然后调整最佳设置,以使警告消失。请注意,在高流量情况下,警告可能会重新出现。

1.1 原因(简短解释)

问题是由于Tomcat无法达到其目标缓存大小而导致的,这是由于缓存条目小于这些条目的TTL。因此,Tomcat没有足够的可以过期的缓存条目,因为它们太新鲜了,所以它不能释放足够的缓存,从而输出警告。
在Tomcat 7中没有出现这个问题,因为Tomcat 7在这种情况下根本不会输出警告。(导致您和我使用贫乏的缓存设置而没有被通知。)
当接收到相对较大数量的HTTP请求以获取资源(通常是静态资源),与缓存的大小和TTL相比,在相对短的时间内,如果缓存达到其最大值(默认为10 MB),并且超过95%的缓存具有新鲜的缓存条目(新鲜意味着缓存中少于5秒),则每次Tomcat尝试在缓存中加载Web资源时都会收到警告消息。

1.2 可选信息

如果您需要在不重新启动服务器的情况下调整cacheMaxSize,请使用JMX。

最快的解决方法是完全禁用缓存:<Resources cachingAllowed="false" />,但这是次优的,因此请按照我刚才描述的增加cacheMaxSize。

2.详细回答

2.1 背景信息

一个WebSource是Web应用程序中的文件或目录。出于性能原因,Tomcat可以缓存WebSources。静态资源缓存的最大值(所有资源总计)默认为10240 kbyte(10 mbyte)。当请求WebResource(例如加载静态图像)时,WebResource将被加载到缓存中,然后称为缓存条目。 每个缓存条目都有一个TTL(存活时间),即缓存条目允许在缓存中停留的时间。当TTL过期时,缓存条目有资格从缓存中删除。cacheTTL的默认值为5000毫秒(5秒)。

还有更多关于缓存的内容,但对于问题来说不相关。

2.2 原因

以下来自Cache类的代码详细显示了缓存策略:
152 // 内容将不会被缓存,但我们仍然需要元数据大小
153 long delta = cacheEntry.getSize();
154 size.addAndGet(delta);
156 if (size.get() > maxSize) {
157 // 为了速度而无序处理资源。在缓存
158 // 效率(年轻的条目可能在旧的条目之前被驱逐
159 // ones)换取速度,因为这是关键路径
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 log.warn(sm.getString("cache.addFail", path));
170 }
171 }
当加载一个web资源时,代码会计算缓存的新大小。如果计算出的大小大于默认的最大大小,则必须删除一个或多个缓存条目,否则新的大小将超过最大值。因此,代码将计算一个“targetSize”,这是缓存希望保持在其下方的大小(作为最佳状态),默认情况下为最大值的95%。为了达到这个目标大小,需要从缓存中删除/驱逐条目。这是使用以下代码完成的:
215  private long evict(long targetSize, Iterator<CachedResource> iter) {
    217      long now = System.currentTimeMillis();
    219      long newSize = size.get();
    221      while (newSize > targetSize && iter.hasNext()) {
        222          CachedResource resource = iter.next();
        224          // 不过期任何在TTL内已被检查的内容
        225          if (resource.getNextCheck() > now) {
            226              continue;
        227          }
        229          // 从缓存中删除该条目
        230          removeCacheEntry(resource.getWebappPath());
        232          newSize = size.get();
    233      }
    235      return newSize;
236  }
注:该代码段是一个Java方法,用于从缓存中清除过多的资源。它遍历缓存并删除最早的资源,直到达到目标大小为止。
当缓存的TTL过期且目标大小尚未达到时,缓存条目将被移除。
在尝试通过驱逐缓存条目来释放缓存后,代码将执行以下操作:
165 如果 (newSize > maxSize) {
166 // 无法为此资源创建足够的空间
167 // 从缓存中删除
168 removeCacheEntry(path);
169 log.warn(sm.getString("cache.addFail", path));
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 秒钟)加载了大量未缓存的 Web 资源(默认最大缓存为 10MB),那么您将收到此警告。

令人困惑的部分是 Tomcat 7 没有显示警告。这只是由于 Tomcat 7 代码造成的:

1606  // 向缓存中添加新条目
1607 synchronized (cache) {
1608 // 检查缓存大小,如果太大则删除元素
1609 if ((cache.lookup(name) == null) && cache.allocate(entry.size)) {
1610 cache.load(entry);
1611 }
1612 }

结合:

231   (toFree > 0) {
232 如果 (attempts == maxAllocateIterations) {
233 // 放弃,不对当前缓存进行任何更改
234 返回 false;
235 }
Tomcat 7在无法释放缓存时不会输出任何警告,而Tomcat 8会输出警告。因此,如果您使用的是与Tomcat 7相同的默认缓存配置的Tomcat 8,并且在Tomcat 8中收到了警告,则Tomcat 7的缓存设置表现不佳,但没有警告。
2.4 解决方案
有多种解决方案:
1.增加缓存(推荐) 2.降低TTL(不推荐) 3.抑制缓存日志警告(不推荐) 4.禁用缓存
2.4.1 增加缓存(推荐)
如此描述:http://tomcat.apache.org/tomcat-8.0-doc/config/resources.html 通过在$CATALINA_BASE/conf/context.xmlContext元素中添加<Resources cacheMaxSize="XXXXX" />,其中“XXXXX”代表以kbytes为单位指定的增加缓存大小。默认值为10240(10兆字节),因此请设置比此更高的大小。

您需要调整最佳设置。请注意,当您突然增加流量/资源请求时,问题可能会重新出现。

为了避免每次想尝试新的缓存大小时都必须重新启动服务器,可以使用JMX在不重新启动的情况下进行更改。

启用 JMX, 在 $CATALINA_BASE/conf/server.xml 中的 Server 元素中添加以下内容: <Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="6767" rmiServerPortPlatform="6768" /> 并从 https://tomcat.apache.org/download-80.cgi 下载 catalina-jmx-remote.jar 并将其放置在 $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禁用缓存存在问题。)

增加缓存?我怀疑这会起作用... 我在源代码中看到了这个: private long maxSize = 10 * 1024 * 1024; http://grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat/tomcat-catalina/8.0.12/org/apache/catalina/webresources/Cache.java - HoaPhan
@HoaPhan 1010241024 是所有缓存的最大值,总共为10MB。根据Web应用程序的流量,这可能在几秒钟内就会达到。将其增加到足够大的程度即可解决问题。 - Devabc
@PHPAvenger 在这种情况下,Tomcat 7根本没有发出警告,而Tomcat 8则会发出警告,因此它可以被视为一种警告功能。问题在于它不仅会在第一次缓存资源请求时发出警告,而且会在每次缓存资源请求时都发出警告。如果只在一定时间或缓存目标命中后发出警告,那么这将是一个改进。 - Devabc
有没有办法可以检查堆上缓存中有什么以及有多少?我们已经将缓存增加到150M,但仍然出现错误。 - Hebbar
在Ubuntu上,context.xml文件的位置是/etc/tomcat9/context.xml。您可能需要调整路径中的Tomcat版本号。 - jbo5112
显示剩余6条评论

169

11
读者可能希望将cacheMaxSize值调整为小于100兆的某个值。 - Eric Spiegelberg
到目前为止,错误消息一直在我的控制台日志中泛滥。现在已经清楚了。谢谢。 - Abubacker Siddik

10

您有更多的静态资源,而缓存空间不足。 您可以执行以下操作之一:

  • 增加缓存大小
  • 减少缓存的TTL
  • 禁用缓存

有关这些配置选项的详细信息,请参见文档


1
感谢您的评论。 我确实理解这个异常的含义,并且当然我读过您提供的文档,但是我不明白为什么在没有进行任何配置更改的情况下,这会从7更改为8。也就是说,为什么默认的文件系统资源处理程序在8和7中会有所不同,而没有任何更改的参考,这是可疑的,因为有一个启动错误被报告并据称已经修复。 - iainmac999
1
也许如果你读了迁移指南 - 尤其是 http://tomcat.apache.org/migration-8.html#Web_application_resources - 事情就会更清楚。 - Mark Thomas
如果文档能够更好地解释a)缓存中涉及的资源以及原因(许多误解围绕着这一点!)和b)不同设置可能产生的影响(例如,盲目地使每个Web应用程序缓存设置变得很大可能会消耗大量内存),并且如何正确地调整这些设置,那么这将是有帮助的。在代码和配置中区分应用程序本身使用的静态资源与用户代理请求并仅由应用程序提供的静态文件也将有所帮助。 - volkerk

6

这并不是解决方案,因为它没有解决导致日志中出现消息的条件,但是可以通过将以下内容附加到conf/logging.properties来抑制该消息:

org.apache.catalina.webresources.Cache.level = SEVERE

这将过滤掉级别为警告的“无法添加资源”的日志。
在我看来,WARNING并不一定是需要解决的错误,如果需要的话可以忽略它。

11
哈哈,这并没有解决问题,只是不显示它。什么鬼! - T3rm1
3
这解决了过度记录的问题,这本身可能是一个重大问题。它回到了以前Tomcat版本的行为,在那里对许多人来说已经足够好了,所以从这个意义上讲,它“解决”了问题。但它并没有解决调整Tomcat缓存的问题,而devabc的答案非常好地涵盖了这个问题。 - volkerk

0
一些更多的提示(我遇到的问题): 如果$CATALINA_BASE/conf/context.xml被Intellij覆盖。
只需在 块内添加: 在您的Tomcat/apache-tomcat-x.x.x/conf/context.xml中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接