流量会通过CDN传递到Apache Web服务器层,该层支持包括调度程序在内的模块。 为了提高性能,调度程序主要用作缓存以限制发布节点上的处理。
可以将规则应用于调度程序配置以修改任何默认的缓存过期设置,从而导致在CDN上缓存。 请注意,如果 enableTTL
在dispatcher配置中启用,这意味着即使在重新发布的内容之外,它也将刷新特定内容。
本页还介绍调度程序缓存如何失效,以及缓存在浏览器级别与客户端库有关的工作方式。
cache-control
apache层发出的标头。 CDN还尊重此价值。DISABLE_DEFAULT_CACHING
变量 global.vars
:Define DISABLE_DEFAULT_CACHING
例如,当您的业务逻辑需要微调页面标题(其值基于日历日)时,这非常有用,因为默认情况下,页面标题设置为0。 话虽如此, 关闭默认缓存时请务必谨慎。
可以通过定义 EXPIRATION_TIME
变量 global.vars
使用AEMas a Cloud ServiceSDK Dispatcher工具。
可以在更精细的粒度级别上覆盖,包括通过以下apache mod_headers指令独立控制CDN和浏览器缓存:
<LocationMatch "^/content/.*\.(html)$">
Header set Cache-Control "max-age=200"
Header set Surrogate-Control "max-age=3600"
Header set Age 0
</LocationMatch>
代理控制标头适用于Adobe管理的CDN。 如果使用 客户管理的CDN,则可能需要不同的标头,具体取决于您的CDN提供商。
在设置全局缓存控制标头或与宽正则表达式匹配的标头时,请务必谨慎,以便这些标头不会应用于您可能打算保留为私有的内容。 请考虑使用多个指令,以确保以细粒度方式应用规则。 根据上述说明,如果AEM as a Cloud Service检测到已将缓存标头应用于Dispatcher检测到的不可执行的内容,则它将删除该缓存标头,如Dispatcher文档中所述。 为了强制AEM始终应用缓存标头,您可以添加 always 选项:
<LocationMatch "^/content/.*\.(html)$">
Header unset Cache-Control
Header unset Expires
Header always set Cache-Control "max-age=200"
Header set Age 0
</LocationMatch>
您必须确保 src/conf.dispatcher.d/cache
具有以下规则(默认配置中):
/0000
{ /glob "*" /type "allow" }
阻止缓存特定内容 在CDN,请将Cache-Control标头设置为 私人. 例如,下面会阻止名为 安全 从缓存到CDN :
<LocationMatch "/content/secure/.*\.(html)$">. // replace with the right regex
Header unset Cache-Control
Header unset Expires
Header always set Cache-Control “private”
</LocationMatch>
其他方法,包括 dispatcher-ttl AEM ACS Commons项目,则无法成功覆盖值。
请注意,调度程序可能仍会根据其自己的内容来缓存内容 缓存规则. 要使内容真正私有,您应确保dispatcher不会缓存该内容。
2022年5月中旬之后创建的程序(特别是高于65000的程序ID)的默认行为是默认缓存,同时尊重请求的身份验证上下文。 默认情况下,旧程序(程序ID等于或低于65000)不会缓存Blob内容。
在这两种情况下,可以使用Apache在Apache/Dispatcher层以更细粒度级别覆盖缓存标头 mod_headers
指令,例如:
<LocationMatch "^/content/.*\.(jpeg|jpg)$">
Header set Cache-Control "max-age=222"
Header set Age 0
</LocationMatch>
修改调度程序层的缓存标头时,请小心不要缓存得太广,请参阅HTML/文本部分中的讨论 以上. 此外,请确保要保持私有(而不是缓存)的资产不属于 LocationMatch
指令筛选器。
AEM层将根据是否已设置缓存标头和请求类型的值来设置缓存标头。 请注意,如果未设置缓存控制标头,则会缓存公共内容,并将经过身份验证的流量设置为私有。 如果已设置缓存控制标头,则缓存标头将不受影响。
是否存在缓存控制标头? | 请求类型 | AEM将缓存标头设置为 |
---|---|---|
否 | 公共 | Cache-Control:public, max-age=600,不可变 |
否 | 已验证 | Cache-Control:private,max-age=600,不可变 |
是 | 任何 | 未更改 |
虽然不推荐,但可以通过设置Cloud Manager环境变量来更改新的默认行为,以遵循旧行为(程序ID等于或小于65000) AEM_BLOB_ENABLE_CACHING_HEADERS
为false。
默认情况下,AEM层不会缓存Blob内容。
建议通过将Cloud Manager环境变量AEM_BLOB_ENABLE_CACHING_HEADERS设置为true,将旧的默认行为更改为与新行为(程序ID大于65000)一致。 如果程序已处于实时状态,请确保您确认在进行更改后,内容会按预期运行。
其他方法,包括 dispatcher-ttl AEM ACS Commons项目,则无法成功覆盖值。
EXPIRATION_TIME
用于html/文本文件类型的变量避免使用 User-Agent
作为 Vary
标题。 旧版默认Dispatcher设置(在原型版本28之前)包含此内容,我们建议您使用以下步骤删除该设置。
<Project Root>/dispatcher/src/conf.d/available_vhosts/*.vhost
Header append Vary User-Agent env=!dont-vary
从所有vhost文件中删除,但default.vhost除外,它为只读使用 Surrogate-Control
用于独立于浏览器缓存控制CDN缓存的标头
考虑应用 stale-while-revalidate
和 stale-if-error
指令,以允许后台刷新并避免缓存缺失,从而让用户快速、新鲜地查看内容。
stale-while-revalidate
对所有缓存控制标头都是一个很好的起点。下面是各种内容类型的一些示例,在设置您自己的缓存规则时,这些内容可用作指南。 请仔细考虑并测试您的具体设置和要求:
缓存12h的可变客户端库资源,12h后进行后台刷新。
<LocationMatch "^/etc\.clientlibs/.*\.(?i:json|png|gif|webp|jpe?g|svg)$">
Header set Cache-Control "max-age=43200,stale-while-revalidate=43200,stale-if-error=43200,public" "expr=%{REQUEST_STATUS} < 400"
Header set Age 0
</LocationMatch>
通过后台刷新,长期(30天)缓存不可变的客户端库资源,以避免MISS。
<LocationMatch "^/etc\.clientlibs/.*\.(?i:js|css|ttf|woff2)$">
Header set Cache-Control "max-age=2592000,stale-while-revalidate=43200,stale-if-error=43200,public,immutable" "expr=%{REQUEST_STATUS} < 400"
Header set Age 0
</LocationMatch>
在浏览器上缓存HTML页面5分钟,后台刷新1小时,在CDN上缓存12小时。 将始终添加Cache-Control标头,因此务必要确保/content/*下匹配的html页面是公共的。 如果没有,请考虑使用更具体的正则表达式。
<LocationMatch "^/content/.*\.html$">
Header unset Cache-Control
Header always set Cache-Control "max-age=300,stale-while-revalidate=3600" "expr=%{REQUEST_STATUS} < 400"
Header always set Surrogate-Control "stale-while-revalidate=43200,stale-if-error=43200" "expr=%{REQUEST_STATUS} < 400"
Header set Age 0
</LocationMatch>
将内容服务/Sling模型导出程序json响应缓存5分钟,在浏览器上刷新1小时,在CDN上刷新12小时。
<LocationMatch "^/content/.*\.model\.json$">
Header set Cache-Control "max-age=300,stale-while-revalidate=3600" "expr=%{REQUEST_STATUS} < 400"
Header set Surrogate-Control "stale-while-revalidate=43200,stale-if-error=43200" "expr=%{REQUEST_STATUS} < 400"
Header set Age 0
</LocationMatch>
长期(30天)通过后台刷新缓存核心图像组件中的不可变URL,以避免发生遗漏。
<LocationMatch "^/content/.*\.coreimg.*\.(?i:jpe?g|png|gif|svg)$">
Header set Cache-Control "max-age=2592000,stale-while-revalidate=43200,stale-if-error=43200,public,immutable" "expr=%{REQUEST_STATUS} < 400"
Header set Age 0
</LocationMatch>
缓存DAM中可变资源(如图像和视频)24小时,并在12小时后进行后台刷新,以避免发生错误
<LocationMatch "^/content/dam/.*\.(?i:jpe?g|gif|js|mov|mp4|png|svg|txt|zip|ico|webp|pdf)$">
Header set Cache-Control "max-age=43200,stale-while-revalidate=43200,stale-if-error=43200" "expr=%{REQUEST_STATUS} < 400"
Header set Age 0
</LocationMatch>
在AdobeCDN中接收HEAD请求时, not 缓存后,调度程序和/或AEM实例将该请求转换并作为GET请求接收。 如果响应可缓存,则随后将从CDN提供HEAD请求。 如果响应不可缓存,则后续HEAD请求将在一段时间内(取决于 Cache-Control
TTL。
通常,不必使调度程序缓存失效。 相反,在重新发布内容时,您应该依赖调度程序刷新其缓存,并依赖CDN遵守缓存过期标头。
与AEM的先前版本一样,发布或取消发布页面将从调度程序缓存中清除内容。 如果怀疑存在缓存问题,客户应重新发布相关页面,并确保虚拟主机与ServerAlias localhost匹配,Dispatcher缓存失效需要该ServerAlias localhost。
当发布实例收到作者提供的页面或资产的新版本时,它会使用刷新代理使其调度程序上的相应路径失效。 更新的路径将从调度程序缓存及其父缓存中删除,最高级别为(您可以使用 stafileslevel)。
Adobe建议依赖标准缓存标头来控制内容交付生命周期。 但是,如果需要,可以直接在Dispatcher中使内容失效。
以下列表包含可能希望显式使缓存失效(同时可选择侦听失效的完成情况)的情景:
显式使缓存失效的方法有两种:
这些方法在层可用性、删除重复事件的能力和事件处理保证方面各不相同。 下表汇总了以下选项:
不适用 | 层可用性 | 重复数据删除 | 担保 | 操作 | 影响 | 描述 |
---|---|---|---|---|---|---|
Sling内容分发(SCD)API | 创作 | 可以使用发现API或启用 重复数据删除模式. | 至少一次。 |
|
|
|
复制API | 发布 | 不可能,在每个发布实例上引发事件。 | 尽力而为。 |
|
|
|
请注意,与缓存失效直接相关的两个操作是Sling Content Distribution(SCD)API Invalidate和Replication API Deactivate。
此外,从桌子上,我们观察到:
当必须保证每个事件都有效时,例如与需要准确知识的外部系统同步时,需要使用SCD API。 请注意,如果在进行失效调用时存在发布层升级事件,则在每个新发布处理失效时将引发其他事件。
使用复制API不是常见的用例,但应在以下情况下使用:使缓存失效的触发器来自发布层,而非创作层。 如果配置了调度程序TTL,则此选项可能会很有用。
最后,如果您希望使Dispatcher缓存失效,建议使用作者的SCD API无效操作。 此外,您还可以侦听事件,以便随后触发进一步的下游操作。
使用下面提供的说明时,请注意,您应在AEM Cloud Service开发环境中测试自定义代码,而不是在本地测试。
使用“作者”的SCD操作时,实施模式如下:
@Reference
private Distributor distributor;
ResourceResolver resolver = ...; // the resource resolver used for authorizing the request
String agentName = "publish"; // the name of the agent used to distribute the request
String pathToInvalidate = "/content/to/invalidate";
DistributionRequest distributionRequest = new SimpleDistributionRequest(DistributionRequestType.INVALIDATE, false, pathToInvalidate);
distributor.distribute(agentName, resolver, distributionRequest);
package org.apache.sling.distribution.journal.shared;
import org.apache.sling.discovery.DiscoveryService;
import org.apache.sling.distribution.journal.impl.event.DistributionEvent;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.sling.distribution.DistributionRequestType.INVALIDATE;
import static org.apache.sling.distribution.event.DistributionEventProperties.DISTRIBUTION_PATHS;
import static org.apache.sling.distribution.event.DistributionEventProperties.DISTRIBUTION_TYPE;
import static org.apache.sling.distribution.event.DistributionEventTopics.AGENT_PACKAGE_DISTRIBUTED;
import static org.osgi.service.event.EventConstants.EVENT_TOPIC;
@Component(immediate = true, service = EventHandler.class, property = {
EVENT_TOPIC + "=" + AGENT_PACKAGE_DISTRIBUTED
})
public class InvalidatedHandler implements EventHandler {
private static final Logger LOG = LoggerFactory.getLogger(InvalidatedHandler.class);
@Reference
private DiscoveryService discoveryService;
@Override
public void handleEvent(Event event) {
String distributionType = (String) event.getProperty(DISTRIBUTION_TYPE);
if (INVALIDATE.name().equals(distributionType)) {
boolean isLeader = discoveryService.getTopology().getLocalInstance().isLeader();
// process the OSGi event on the leader author instance
if (isLeader) {
String[] paths = (String[]) event.getProperty(DISTRIBUTION_PATHS);
String packageId = (String) event.getProperty(DistributionEvent.PACKAGE_ID);
invalidated(paths, packageId);
}
}
}
private void invalidated(String[] paths, String packageId) {
// custom logic
LOG.info("Successfully applied package with id {}, paths {}", packageId, paths);
}
}
invalidated(String[] paths, String packageId)
方法。调度程序失效时,不会刷新AdobeCDN。 Adobe管理的CDN遵循TTL,因此无需刷新。
下面介绍了使用复制API停用操作时的实施模式:
无法配置刷新代理端点,但是进行了预配置,以指向与与刷新代理一起运行的发布服务相匹配的调度程序。
刷新代理通常可由基于OSGi事件或工作流的自定义代码触发。
String[] paths = …
ReplicationOptions options = new ReplicationOptions();
options.setSynchronous(true);
options.setFilter( new AgentFilter {
public boolean isIncluded (Agent agent) {
return agent.getId().equals(“flush”);
}
});
Replicator.replicate (session,ReplicationActionType.DELETE,paths, options);
页面由HTML、Javascript、CSS和图像组成。 我们鼓励客户利用 客户端库(clientlibs)框架 将Javascript和CSS资源导入HTML页面,同时考虑JS库之间的依赖关系。
clientlibs框架提供了自动版本管理,这意味着开发人员可以在源代码管理中签入对JS库的更改,并且当客户推送其版本时,将提供最新版本。 如果没有这些权限,开发人员将需要手动更改引用新版库的HTML,如果许多HTML模板共享同一库,则更加繁琐。
将库的新版本发布到生产环境后,将更新引用的HTML页面,其中包含指向这些更新库版本的新链接。 给定HTML页面的浏览器缓存过期后,无论从浏览器缓存中加载旧库,因为现在保证(从AEM中)刷新的页面会引用库的新版本。 换言之,刷新的HTML页面将包含所有最新的库版本。
其机制是序列化哈希,将附加到客户端库链接中,以确保浏览器有一个唯一的版本化URL来缓存CSS/JS。 仅当客户端库的内容发生更改时,才会更新序列化的哈希。 这意味着,即使在新部署中,如果发生不相关的更新(即对客户端库的基础css/js没有更改),则引用将保持不变,从而确保减少对浏览器缓存的中断。
HTML页面上的默认clientlib包含如下示例所示:
<link rel="stylesheet" href="/etc.clientlibs/wkndapp/clientlibs/clientlib-base.css" type="text/css">
启用严格clientlib版本控制后,会将长期哈希键作为选择器添加到客户端库。 因此,clientlib引用如下所示:
<link rel="stylesheet" href="/etc.clientlibs/wkndapp/clientlibs/clientlib-base.lc-7c8c5d228445ff48ab49a8e3c865c562-lc.css" type="text/css">
默认情况下,所有AEMas a Cloud Service环境中都启用了严格clientlib版本控制。
要在本地SDK快速启动中启用严格的clientlib版本控制,请执行以下操作:
<host>/system/console/configMgr