从 Tomcat 7 升级到 8 时,我遇到了同样的问题:关于缓存的大量日志警告持续泛滥。
1。简答
在$CATALINA_BASE/conf/context.xml 的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。
最快的解决方法是完全禁用缓存:<Resources cachingAllowed="false" />,但这不是最理想的,所以按照我刚才的描述增加 cacheMaxSize。
2。长答案
2.1 背景资料
WebSource 是 Web 应用程序中的文件或目录。出于性能原因,Tomcat 可以缓存 WebSource。 maximum of the static resource cache(所有资源总计)默认为 10240 kbyte (10 mbyte)。当请求 webResource 时(例如加载静态图像时),将 webResource 加载到缓存中,然后将其称为缓存条目。
每个缓存条目都有一个TTL(生存时间),这是允许缓存条目留在缓存中的时间。当 TTL 过期时,缓存条目有资格从缓存中删除。 cacheTTL 的默认值为 5000 毫秒(5 秒)。
还有更多关于缓存的内容,但这与问题无关。
2.2 原因
Cache class 中的以下代码详细显示了缓存策略:
152 // 内容不会被缓存,但我们仍然需要元数据大小
153 long delta = cacheEntry.getSize();
154 size .addAndGet(delta);
156 if (size.get() > maxSize) {
157 // 处理速度无序的资源。交易缓存
158 // 效率(较年轻的条目可能在较旧的条目之前被逐出
159 // )以换取速度,因为这是
160 // 请求处理的关键路径161 long targetSize =
162 maxSize * (100 - TARGET_FREE_PERCENT_GET) / 100;
163 long newSize = evict (
164targetSize, resourceCache.values().iterator());
165 if (newSize > maxSize) {
166 // 无法为此资源创建足够的空间
167 // 将其从缓存中删除
168 removeCacheEntry(path);
169 log.warn(sm. getString("cache.addFail", path));
170 }
171 }
加载 webResource 时,代码会计算缓存的新大小。如果计算的大小大于默认的最大大小,则必须删除一个或多个缓存条目,否则新大小将超过最大值。所以代码将计算一个“targetSize”,这是缓存想要保持的大小(作为最佳值),默认为最大值的 95%。为了达到这个 targetSize,必须从缓存中删除/逐出条目。这是使用以下代码完成的:
215 private long evict(long targetSize, IteratorCachedResource> iter) {
@987654357 @ long now = System.currentTimeMillis();
219 long newSize = size.get();
221 while (newSize > targetSize && iter.hasNext()) {
222 CachedResource resource = iter.next();
224 // 不要' t 过期 TTL 内检查的任何内容
225 if (resource.getNextCheck() > now) {
226 continue ;
227 }
229 // 从缓存中删除条目
230 removeCacheEntry(resource.getWebappPath());
232 newSize = size .get();
233 }
235 返回 newSize;
236 }
因此,当缓存条目的 TTL 过期且尚未达到 targetSize 时,将删除缓存条目。
在尝试通过逐出缓存条目来释放缓存后,代码将执行以下操作:
165 if (newSize > maxSize) {
166 // 无法为该资源创建足够的空间
167 // 将其从缓存中移除168removeCacheEntry(路径);
169log.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 秒)内加载了大量未缓存的 webResources(大约缓存的最大值,默认为 10mb),那么您将收到警告。
令人困惑的部分是 Tomcat 7 没有显示警告。这仅仅是由这个 Tomcat 7 代码引起的:
1606 // 向缓存添加新条目
1607 同步 (缓存) {
1608 // 检查缓存大小,如果太大则删除元素1609 if ((cache.lookup(name) == null) && cache.allocate(entry.size)) {
1610 cache.load(entry);
1611 }
1612 }
结合:
231 while (toFree > 0) {
232 if (attempts == maxAllocateIterations) {
233 // 给up,不会对当前缓存做任何更改
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
通过在$CATALINA_BASE/conf/context.xml 的Context 元素中添加<Resources cacheMaxSize="XXXXX" />,其中“XXXXX”代表增加的缓存大小,以千字节为单位。默认值为 10240(10 mbyte),因此请设置比此更大的大小。
您必须调整以获得最佳设置。请注意,当您突然增加流量/资源请求时,问题可能会再次出现。
为避免每次尝试新的缓存大小时都必须重新启动服务器,您可以使用 JMX 更改它而无需重新启动。
对于enable 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 禁用缓存可能会出现问题。)