Tomcat 8 throw-org.apache.catalina.webresources. Cache.getResource 无法添加资源

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

我得到了很多静态图像文件:

无法添加 资源[/base/1325/WA6144-150x112. jpg ]到缓存,因为在缓存中 驱逐过期缓存后可用空间不足 条目-考虑增加缓存的最大大小

我没有指定任何特定的资源设置,而且我在7.0.52中也没有得到这个设置。

我发现在一个据说已经修复的 bug 报告中提到了在启动时发生的这种情况。对我来说,这种情况不是在启动时发生,而是在请求资源时不断发生。

还有其他人有这个问题吗?

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

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

谢谢。

221511 次浏览

您拥有更多的静态资源,缓存可以容纳这些静态资源:

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

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

我在从 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(10mbyte) ,所以设置一个比这个大的值。然后调优警告消失的最佳设置。 请注意,警告可能会在更高的流量情况下返回。

1.1原因(简短解释)

这个问题是由于 Tomcat 无法达到目标缓存大小,因为缓存条目小于这些条目的 TTL。因此 Tomcat 没有足够的缓存条目可以过期,因为它们太新了,所以它不能释放足够的缓存,从而输出警告。

这个问题在 Tomcat7中没有出现,因为 Tomcat7在这种情况下根本没有输出警告。(导致您和我在没有得到通知的情况下使用糟糕的缓存设置。)

与缓存的大小和 TTL 相比,在相对较短的时间内接收到相对大量的对资源的 HTTP 请求(通常是静态的)时,就会出现问题。如果缓存达到了最大值(默认为10mb) ,其大小的95% 以上都有新的缓存条目(新的意味着缓存中的时间少于5秒) ,那么对于 Tomcat 试图加载到缓存中的每个 webResource,您都会收到一条警告消息。

1.2可选信息

如果需要在正在运行的服务器上调优 cacheMaxSize,而不需要重新启动它,请使用 JMX。

最快的解决方案是完全禁用 cache: <Resources cachingAllowed="false" />,但这不是最佳方案,因此按照我刚才描述的那样增加 cache MaxSize。

2. 长答案

2.1背景资料

网络资源是 Web 应用程序中的文件或目录。出于性能原因,Tomcat 可以缓存 WebSource。默认情况下,静态资源缓存的最大值(总共所有资源)为10240kbyte (10mbyte)。当请求 webResource 时(例如在加载静态映像时) ,将 webResource 加载到缓存中,然后将其称为缓存条目。 每个缓存条目都有一个 TTL(time to live) ,这是允许缓存条目保留在缓存中的时间。当 TTL 过期时,缓存条目有资格从缓存中删除。Cache TTL 的默认值为5000毫秒(5秒)。

关于缓存还有很多要讲的,但这与问题无关。

2.2原因

下面来自 缓存类的代码详细显示了缓存策略:

152  // Content will not be cached but we still need metadata size
153 long delta = cacheEntry.getSize();
154 size.addAndGet(delta);
156 if (size.get() > maxSize) {
157 // Process resources unordered for speed. Trades cache
158 // efficiency (younger entries may be evicted before older
159 // ones) for speed since this is on the critical path for
160 // request processing
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 // Unable to create sufficient space for this resource
167 // Remove it from the cache
168 removeCacheEntry(path);
169 log.warn(sm.getString("cache.addFail", path));
170 }
171 }

加载 webResource 时,代码计算缓存的新大小。如果计算的大小大于默认的最大大小,则必须删除一个或多个缓存项,否则新的大小将超过最大值。因此代码将计算一个“ targetSize”,这是缓存希望保持的大小(作为最佳值) ,默认情况下是最大值的95% 。为了达到这个 targetSize,必须从缓存中删除/驱逐条目。这是使用以下代码完成的:

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 // Don't expire anything that has been checked within the TTL
225 if (resource.getNextCheck() > now) {
226 continue;
227 }
229 // Remove the entry from the cache
230 removeCacheEntry(resource.getWebappPath());
232 newSize = size.get();
233 }
235 return newSize;
236 }

因此,当缓存条目的 TTL 过期且 targetSize 尚未到达时,将删除该条目。

在尝试通过驱逐缓存条目释放缓存之后,代码将执行以下操作:

165  if (newSize > maxSize) {
166 // Unable to create sufficient space for this resource
167 // Remove it from the cache
168 removeCacheEntry(path);
169 log.warn(sm.getString("cache.addFail", path));
170 }

So if after the attempt to free cache, the size still exceeds the maximum, it will show the warning message about being unable to free:

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  // Add new entry to cache
1607 synchronized (cache) {
1608 // Check cache size, and remove elements if too big
1609 if ((cache.lookup(name) == null) && cache.allocate(entry.size)) {
1610 cache.load(entry);
1611 }
1612 }

combined with:

231  while (toFree > 0) {
232 if (attempts == maxAllocateIterations) {
233 // Give up, no changes are made to the current cache
234 return 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.xml中的 Context元素中添加 <Resources cacheMaxSize="XXXXX" />,其中“ XXXXX”表示增加的缓存大小,以千字节表示。默认值是10240(10mbyte) ,因此设置的大小应高于此值。

你必须调整到最佳设置。请注意,当流量/资源请求突然增加时,问题可能会再次出现。

为了避免每次尝试新的缓存大小时都必须重新启动服务器,可以使用 JMX 更改服务器而无需重新启动。

对于 启用 JMX,在 Server元素内将其添加到 $CATALINA_BASE/conf/server.xml: 从 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" />

尽管我记得在 Tomcat8的 beta 版本中,我使用 JMX 禁用了缓存。(不确定确切的原因,但是通过 server.xml 禁用缓存可能有问题。)

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

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

这将过滤掉“ Unable to add the resource”日志,这些日志处于警告级别。

在我看来,WARNING不一定是一个需要处理的错误,而是可以忽略的,如果需要的话。

$CATALINA_BASE/conf/context.xml中,在 </Context>之前添加块

<Resources cachingAllowed="true" cacheMaxSize="100000" />

更多信息: http://tomcat.apache.org/tomcat-8.0-doc/config/resources.html