当我从Tomcat 7升级到8时,遇到了同样的问题:关于缓存的大量日志警告。
1. 简短回答
在$CATALINA_BASE/conf/context.xml
的Context
xml元素中添加以下内容:
<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.xml
的
Context
元素中添加
<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禁用缓存存在问题。)