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

标签 caching resources tomcat8

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

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

org.apache.catalina.webresources.Cache.getResource Unable to add the resource at [/base/1325/WA6144-150x112.jpg] to the cache because there was insufficient free space available after evicting expired cache entries - consider increasing the maximum size of the cache



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

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

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

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

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

谢谢。

最佳答案

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

1. 简答

将此添加到 Context 中您的 $CATALINA_BASE/conf/context.xml 的 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 在这种情况下根本没有输出警告。 (导致你和我在没有得到通知的情况下使用糟糕的缓存设置。)

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

1.2 可选信息

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

最快的解决方法是完全禁用缓存:<Resources cachingAllowed="false" /> ,但这是次优的,所以像我刚刚描述的那样增加 cacheMaxSize 。

2. 长答案

2.1 背景资料

WebSource是 Web 应用程序中的文件或目录。出于性能原因,Tomcat 可以缓存 WebSources。 maximum of the static resource cache (所有资源的总和)默认为 10240 KB(10 兆字节)。当 webResource 被请求时(例如加载静态图像时),webResource 被加载到缓存中,然后它被称为缓存条目。
每个缓存条目都有一个 TTL (time to live),即缓存条目被允许留在缓存中的时间。当 TTL 到期时,缓存条目有资格从缓存中删除。 cacheTTL 的默认值为 5000 毫秒(5 秒)。

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

2.2 原因

以下代码来自 Cache class详细显示缓存策略:
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

2.3 问题

所以正如警告信息所说,问题是

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



如果您的 Web 应用程序在短时间内(5 秒)加载了大量未缓存的 webResources(大约最大缓存,默认为 10mb),那么您将收到警告。

令人困惑的部分是 Tomcat 7 没有显示警告。这只是由这个 Tomcat 7 代码引起的:

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

结合:

231而 (toFree > 0) { 232如果(尝试数 == maxAllocateIterations){ 233//放弃,当前缓存不做任何改动 234返回假; 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 更改它而无需重新启动。

    enable JMX ,将此添加到 $CATALINA_BASE/conf/server.xmlServer元素:<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" />
    这实际上归结为在 ram 中拥有和填充缓存而不使用它。

    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 的测试版中,我使用 JMX 来禁用缓存。 (不知道究竟是为什么,但通过 server.xml 禁用缓存可能会出现问题。)

    关于caching - Tomcat 8 抛出 - org.apache.catalina.webresources.Cache.getResource 无法添加资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26893297/

    相关文章:

    java - Hazelcast 内存不足

    tomcat - 从对象工厂访问 Tomcat JNDI 资源

    html - Tomcat7 -> Tomcat8 - 服务器只打印一半的 html 页面

    linux - 允许非root用户使用端口443运行tomcat

    php - 对于高度变化的网站,memcache 或 memcached 有什么好处?

    java - 如果我的数据库关闭,取消 EhCache 中的 timeToLive 缓存过期?

    c# - Stackexchange Redis 哨兵客户端

    php - 复制php资源

    angularjs - 如何在没有方法的情况下将 Angular $resource 对象转换为常规对象

    java - 缺少 tomcat8.5 的 Jar 文件