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

iai*_*999 106 resources caching tomcat8

我刚刚将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"
/>  
Run Code Online (Sandbox Code Playgroud)

谢谢.

Des*_*ica 154

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

<Resources cachingAllowed="true" cacheMaxSize="100000" />
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请访问:http://tomcat.apache.org/tomcat-8.0-doc/config/resources.html

  • 个别读者可能希望将cacheMaxSize值调整为小于100兆的值. (11认同)

Dev*_*abc 124

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

解决方案(TL; DR)

最好的解决方案是通过添加来增加缓存

<!-- 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" />
Run Code Online (Sandbox Code Playgroud)

Context元素中$CATALINA_BASE/conf/context.xml,其中"XXXXX"代表增加的高速缓存大小,以kbytes为单位.默认值为10240(10 MB),因此请设置大于此值的大小.然后调整以获得警告消失的最佳设置.请注意,警告可能会在高流量情况下返回.

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

最快的解决方法是完全禁用缓存:10240,但这不是最理想的,所以按照我刚才描述的方式增加它.

原因(简要)

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

问题没有出现在Tomcat 7中,因为Tomcat 7在这种情况下根本没有输出警告.(导致你和我在没有得到通知的情况下使用较差的缓存设置.)

The problem appears when receiving a relative large amount of HTTP requests for resources (usually static) in a relative short time period compared to the size and TTL of the cache. If the cache is reaching its maximum (10mb by default) with more than 95% of its size with fresh cache entries (fresh means less than less than 5 seconds in cache), than you will get a warning message for each webResource that Tomcat tries to load in the cache.

Background information

A WebSource is a file or directory in a web application. For performance reasons, Tomcat can cache WebSources. The maximum of the static resource cache (all resources in total) is by default 10240 kbyte (10 mbyte). A webResource is loaded into the cache when the webResource is requested (for example when loading a static image), it's then called a cache entry. Every cache entry has a TTL (time to live), which is the time that the cache entry is allowed to stay in the cache. When the TTL expires, the cache entry is eligible to be removed from the cache. The default value of the cacheTTL is 5000 milliseconds (5 seconds).

There is more to tell about caching, but that is irrelevant for the problem.

The cause (detailed)

The following code from the Cache class shows the caching policy in detail:

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 }

When loading a webResource, the code calculates the new size of the cache. If the calculated size is larger than the default maximum size, than one or more cached entries have to be removed, otherwise the new size will exceed the maximum. So the code will calculate a "targetSize", which is the size the cache wants to stay under (as an optimum), which is by default 95% of the maximum. In order to reach this targetSize, entries have to be removed/evicted from the cache. This is done using the following code:

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 }

So a cache entry is removed when its TTL is expired and the targetSize hasn't been reached yet.

After the attempt to free cache by evicting cache entries, the code will do:

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
Run Code Online (Sandbox Code Playgroud)

The problem

So as the warning message says, the problem is

insufficient free space available after evicting expired cache entries - consider increasing the maximum size of the cache

If your web application loads a lot of uncached webResources (about maximum of cache, by default 10mb) within a short time (5 seconds), then you'll get the warning.

The confusing part is that Tomcat 7 didn't show the warning. This is simply caused by this Tomcat 7 code:

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 }

So Tomcat 7 simply doesn't output any warning at all when it's unable to free cache, whereas Tomcat 8 will output a warning.

So if you are using Tomcat 8 with the same default caching configuration as Tomcat 7, and you got warnings in Tomcat 8, than your (and mine) caching settings of Tomcat 7 were performing poorly without warning.

Solutions

There are multiple solutions:

  1. Increase cache (recommended)
  2. Lower the TTL (not recommended)
  3. Suppress cache log warnings (not recommended)
  4. Disable cache

1. Increase cache (recommended)

As described here: http://tomcat.apache.org/tomcat-8.0-doc/config/resources.html

By adding <Resources cachingAllowed="false" /> within the <Resources cacheMaxSize="XXXXX" /> element in Context, where "XXXXX" stands for an increased cache size, specified in kbytes. The default is 10240 (10 mbyte), so set a size higher than this.

You'll have to tune for optimum settings. Note that the problem may come back when you suddenly have an increase in traffic/resource requests.

To avoid having to restart the server every time you want to try a new cache size, you can change it without restarting by using JMX.

To enable JMX, add this to $CATALINA_BASE/conf/context.xml within the $CATALINA_BASE/conf/server.xml element: Server and download <Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="6767" rmiServerPortPlatform="6768" /> from https://tomcat.apache.org/download-80.cgi and put it in catalina-jmx-remote.jar. Then use jConsole (shipped by default with the Java JDK) to connect over JMX to the server and look through the settings for settings to increase the cache size while the server is running. Changes in these settings should take affect immediately.

2. Lower the TTL (not recommended)

Lower the $CATALINA_HOME/lib value by something lower than 5000 milliseconds and tune for optimal settings.

For example: cacheTtl

This comes effectively down to having and filling a cache in ram without using it.

3. Suppress cache log warnings (not recommended)

Configure logging to disable the logger for <Resources cacheMaxSize="2000" />.

For more info about logging in Tomcat: http://tomcat.apache.org/tomcat-8.0-doc/logging.html

4. Disable cache

You can disable the cache by setting org.apache.catalina.webresources.Cache to cachingAllowed. false

Although I can remember that in a beta version of Tomcat 8, I was using JMX to disable the cache. (Not sure why exactly, but there may be a problem with disabling the cache via server.xml.)


Mar*_*mas 9

您有更多静态资源,缓存有空间.您可以执行以下操作之一:

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

有关更多详细信息,请参阅这些配置选项的文档.


Geo*_*oth 6

万一它可以帮助其他人,我能够解决这个问题的唯一方法是将以下内容附加到conf/logging.properties:

org.apache.catalina.webresources.Cache.level = SEVERE
Run Code Online (Sandbox Code Playgroud)

这会过滤掉"无法添加资源"日志,这些日志处于警告级别.

  • 哈哈.这并没有解决问题.它只是没有显示出来.WTF! (9认同)
  • 这解决了过度日志记录的问题,这本身就可能是一个严重的问题。它返回到以前的 tomcat 版本的行为,对于许多人来说,事情运行得足够好,所以从这个意义上说,它“解决”了问题。它没有解决实际调整 tomcat 缓存的问题,devabc 的答案很好地涵盖了这个问题。 (2认同)