Dispatcher 版本独立于 AEM。您可能是在单击 AEM 或 CQ 文档中嵌入的 Dispatcher 文档链接后重定向到此页面。
使用 Dispatcher 可处理多个 Web 域中的页面请求,并支持以下条件:
例如,一家公司针对其两个品牌发布网站:品牌 A 和品牌 B。网站页面的内容是在 AEM 中创作的,并且将存储在同一存储库工作区中:
/
| - content
| - sitea
| - content nodes
| - siteb
| - content nodes
BrandA.com
的页面存储在 /content/sitea
下方。针对 URL https://BrandA.com/en.html
的客户端请求将返回到 /content/sitea/en
节点的渲染页面。同样,BrandB.com
的页面存储在 /content/siteb
下方。
在使用 Dispatcher 缓存内容时,必须在客户端 HTTP 请求中的页面 URL 与缓存中对应文件的路径/存储库中对应文件的路径之间建立关联。
当客户端向 Web 服务器发送 HTTP 请求时,请求页面的 URL 必须解析为 Dispatcher 缓存中的内容,并最终解析为存储库中的内容。
当 Dispatcher Flush 复制代理请求 Dispatcher 使缓存的文件失效时,存储库中内容的路径必须解析为缓存中的内容。
要在多个域中使用 Dispatcher,您需要配置 AEM、Dispatcher 和您的 Web 服务器。此页面上描述的解决方案是通用的,适用于大多数环境。由于一些 AEM 拓扑的复杂性,您的解决方案可能需要进一步的自定义配置才能解决特定问题。您可能需要调整示例以符合现有的 IT 基础架构和管理策略。
要使域 URL 和内容路径能够解析为缓存的文件,必须在此过程中的某个时间点转换文件路径或页面 URL。提供了以下常见策略的说明,其中将在此流程的不同时间点转换路径或 URL:
通常最好是使用网页的短 URL。通常,页面 URL 反映了包含 Web 内容的存储库文件夹的结构。但是,URL 不会显示最顶层的存储库节点,例如 /content
。客户端不一定了解 AEM 存储库的结构。
您的环境必须实施以下配置来支持在多个域中使用 Dispatcher:
提供的示例解决方案适用于具有以下特征的环境:
在 Linux 系统上部署 AEM 创作和发布实例。
Apache HTTPD 是 Linux 系统上部署的 Web 服务器。
AEM 内容存储库和 Web 服务器的文档根目录使用以下文件结构(Apache Web Server 的文档根目录为 /usr/lib/apache/httpd-2.4.3/htdocs)
:
存储库
| - /content
| - sitea
| | - content nodes
| - siteb
| - content nodes
Web 服务器的文档根目录
| - /usr
| - lib
| - apache
| - httpd-2.4.3
| - htdocs
| - content
| - sitea
| - content nodes
| - siteb
| - content nodes
用于资源解析的 Sling 映射可让您将传入 URL 与 AEM 内容路径相关联。在 AEM 发布实例上创建映射,以使来自 Dispatcher 的渲染请求解析为存储库中的正确内容。
针对页面渲染的 Dispatcher 请求使用从 Web 服务器传递的 URL 标识页面。当 URL 包含域名时,Sling 映射会将 URL 解析为内容。下图说明了 branda.com/en.html
URL 与 /content/sitea/en
节点之间的映射。
Dispatcher 缓存反映了存储库节点结构。因此,在页面激活时,产生的使缓存页面失效的请求不需要 URL 或路径转换。
在 Web 服务器上定义虚拟主机,以便向每个 Web 域分配不同的文档根目录:
以下示例 httpd.conf
文件为 Apache Web Server 配置两个虚拟域:
对于此配置,Web 服务器会在收到 https://branda.com/en/products.html
的请求时执行以下操作:
将 URL 与 ServerName
为 branda.com.
的虚拟主机关联。
将 URL 转发给 Dispatcher。
# load the Dispatcher module
LoadModule dispatcher_module modules/mod_dispatcher.so
# configure the Dispatcher module
<IfModule disp_apache2.c>
DispatcherConfig conf/dispatcher.any
DispatcherLog logs/dispatcher.log
DispatcherLogLevel 3
DispatcherNoServerHeader 0
DispatcherDeclineRoot 0
DispatcherUseProcessedURL 0
DispatcherPassError 0
</IfModule>
# Define virtual host for brandA.com
<VirtualHost *:80>
ServerName branda.com
DocumentRoot /usr/lib/apache/httpd-2.4.3/htdocs/content/sitea
<Directory /usr/lib/apache/httpd-2.4.3/htdocs/content/sitea>
<IfModule disp_apache2.c>
SetHandler dispatcher-handler
ModMimeUsePathInfo On
</IfModule>
Options FollowSymLinks
AllowOverride None
</Directory>
</VirtualHost>
# define virtual host for brandB.com
<VirtualHost *:80>
ServerName brandB.com
DocumentRoot /usr/lib/apache/httpd-2.4.3/htdocs/content/siteb
<Directory /usr/lib/apache/httpd-2.4.3/htdocs/content/siteb>
<IfModule disp_apache2.c>
SetHandler dispatcher-handler
ModMimeUsePathInfo On
</IfModule>
Options FollowSymLinks
AllowOverride None
</Directory>
</VirtualHost>
# document root for web server
DocumentRoot "/usr/lib/apache/httpd-2.4.3/htdocs"
请注意,虚拟主机将继承主服务器部分中配置的 DispatcherConfig 属性值。虚拟主机可以包括自己的 DispatcherConfig 属性来覆盖主服务器配置。
要支持包含域名及其相应的虚拟主机的 URL,请定义以下 Dispatcher 场:
虚拟主机的场必须具有以下配置,以使客户端 HTTP 请求中的 URL 解析为 Dispatcher 缓存中的正确文件:
/virtualhosts
属性设置为域名。利用此属性,Dispatcher 能够将场与域相关联。
利用 /filter
属性可访问域名部分后截断的请求 URL 的路径。例如,对于 https://branda.com/en.html
URL,路径将解释为 /en.html
,因此过滤器必须允许对此路径的访问。
/docroot
属性设置为 Dispatcher 缓存中的域站点内容的根目录路径。此路径用作原始请求中的连接的 URL 的前缀。例如,docroot 为 /usr/lib/apache/httpd-2.4.3/htdocs/sitea
会导致针对 https://branda.com/en.html
的请求解析为 /usr/lib/apache/httpd-2.4.3/htdocs/sitea/en.html
文件。
此外,必须将 AEM 发布实例指定为虚拟主机的渲染器。根据需要配置其他场属性。以下代码是 branda.com 域的缩写场配置:
/farm_sitea {
...
/virtualhosts { "branda.com" }
/renders {
/rend01 { /hostname "127.0.0.1" /port "4503" }
}
/filter {
/0001 { /type "deny" /glob "*" }
/0023 { /type "allow" /glob "*/en*" }
...
}
/cache {
/docroot "/usr/lib/apache/httpd-2.4.3/htdocs/content/sitea"
...
}
...
}
需要 Dispatcher 场来处理使缓存文件失效的请求。该场必须能够访问每个虚拟主机的 docroot 目录中的 .stat 文件。
利用以下属性配置,Dispatcher 能够从缓存中的文件解析 AEM 内容存储库中的文件:
/docroot
属性设置为 Web 服务器的默认 docroot。通常,这是在其中创建 /content
文件夹的目录。Linux 上的 Apache 示例值为 /usr/lib/apache/httpd-2.4.3/htdocs
。/filter
属性允许对 /content
目录下的文件的访问。/statfileslevel
属性必须足够高,才会在每个虚拟主机的根目录中创建 .stat 文件。利用此属性,可单独使每个域的缓存失效。对于示例设置,/statfileslevel
值为 2
将在 *docroot*/content/sitea
目录和 *docroot*/content/siteb
目录中创建 .stat 文件。
此外,必须将发布实例指定为虚拟主机的渲染器。根据需要配置其他场属性。以下代码是用于使缓存失效的场的缩写配置:
/farm_flush {
...
/virtualhosts { "invalidation_only" }
/renders {
/rend01 { /hostname "127.0.0.1" /port "4503" }
}
/filter {
/0001 { /type "deny" /glob "*" }
/0023 { /type "allow" /glob "*/content*" }
...
}
/cache {
/docroot "/usr/lib/apache/httpd-2.4.3/htdocs"
/statfileslevel "2"
...
}
...
}
在启动 Web 服务器时,Dispatcher 日志(在调试模式下)指示所有场的初始化:
Dispatcher initializing (build 4.1.2)
[Fri Nov 02 16:27:18 2012] [D] [24974(140006182991616)] farms[farm_sitea].cache.docroot = /usr/lib/apache/httpd-2.4.3/htdocs/content/sitea
[Fri Nov 02 16:27:18 2012] [D] [24974(140006182991616)] farms[farm_siteb].cache.docroot = /usr/lib/apache/httpd-2.4.3/htdocs/content/siteb
[Fri Nov 02 16:27:18 2012] [D] [24974(140006182991616)] farms[farm_flush].cache.docroot = /usr/lib/apache/httpd-2.4.3/htdocs
[Fri Nov 02 16:27:18 2012] [I] [24974(140006182991616)] Dispatcher initialized (build 4.1.2)
使用 Sling 映射进行资源解析,使基于域的 URL 解析为 AEM 发布实例上的内容。资源映射将来自 Dispatcher(最初来自客户端 HTTP 请求)的传入 URL 转换为内容节点。
要了解 Sling 资源映射,请参阅 Sling 文档中的用于资源解析的映射。
通常,以下资源需要映射,但也可能需要额外的映射:
/content
的下方)/etc/designs
的下方)/libs
文件夹创建内容页面的映射后,要发现其他所需的映射,请使用 Web 浏览器打开 Web 服务器上的页面。在发布实例的 error.log 文件中,找到有关未找到的资源的消息。以下示例消息指示需要 /etc/clientlibs
的映射:
01.11.2012 15:59:24.601 *INFO* [10.36.34.243 [1351799964599] GET /etc/clientlibs/foundation/jquery.js HTTP/1.1] org.apache.sling.engine.impl.SlingRequestProcessorImpl service: Resource /content/sitea/etc/clientlibs/foundation/jquery.js not found
默认 Apache Sling 重写器的 linkchecker 转换器会自动修改页面中的超链接以防止链接失效。但是,仅当链接目标是 HTML 或 HTM 文件时才执行链接重写。要更新指向其他文件类型的链接,请创建转换器组件并将它添加到 HTML 重写器管道中。
下表列出了实施 branda.com 域的资源映射的节点。为 brandb.com
域创建类似的节点,例如 /etc/map/http/brandb.com
。在所有情况下,只要无法在 Sling 的上下文中正确解析页面 HTML 中的引用,就需要映射。
节点路径 | 类型 | 属性 |
---|---|---|
/etc/map/http/branda.com |
sling:Mapping | 名称:sling:internalRedirect 类型:字符串 值:/content/sitea |
/etc/map/http/branda.com/libs |
sling:Mapping | 名称:sling:internalRedirect 类型:字符串 值:/libs |
/etc/map/http/branda.com/etc |
sling:Mapping | |
/etc/map/http/branda.com/etc/designs |
sling:Mapping | 名称:sling:internalRedirect 类型:字符串 值:/etc/designs |
/etc/map/http/branda.com/etc/clientlibs |
sling:Mapping | 名称:sling:internalRedirect 类型:字符串 值:/etc/clientlibs |
AEM 发布实例上的 Dispatcher Flush 复制代理必须将失效请求发送到正确的 Dispatcher 场。要将场作为目标,请使用 Dispatcher Flush 复制代理的 URI 属性(在“传输”选项卡上)。包含为使缓存失效而配置的 Dispatcher 场的 /virtualhost
属性值:
https://*webserver_name*:*port*/*virtual_host*/dispatcher/invalidate.cache
例如,要使用上一示例的 farm_flush
场,URI 应为 https://localhost:80/invalidation_only/dispatcher/invalidate.cache
。
使用 Web 服务器的内部 URL 重写功能将基于域的 URL 转换为 Dispatcher 缓存中的文件路径。例如,https://brandA.com/en.html
页面的客户端请求将转换为 Web 服务器的文档根目录中的 content/sitea/en.html
文件。
Dispatcher 缓存反映了存储库节点结构。因此,在页面激活时,产生的使缓存页面失效的请求不需要 URL 或路径转换。
在 Web 服务器上进行如下配置:
DispatcherUseProcessedURL
属性。)以下示例 httpd.conf 文件为 Apache Web Server 配置两个虚拟主机:
服务器名称(与域名一致)是 brandA.com
(第 16 行)和 brandB.com
(第 32 行)。
每个虚拟域的文档根目录是 Dispatcher 缓存中包含站点页面的目录。(第 20 行和第 33 行)
每个虚拟域的 URL 重写规则是一个正则表达式,它会在请求页面的路径前面加上缓存中的页面路径。(第 19 行和第 35 行)
DispatherUseProcessedURL
属性设置为 1
。(第 10 行)
例如,Web 服务器会在收到带 https://brandA.com/en/products.html
URL 的请求时执行以下操作:
ServerName
为 brandA.com.
的虚拟主机关联。/content/sitea/en/products.html.
# load the Dispatcher module
LoadModule dispatcher_module modules/mod_dispatcher.so
# configure the Dispatcher module
<IfModule disp_apache2.c>
DispatcherConfig conf/dispatcher.any
DispatcherLog logs/dispatcher.log
DispatcherLogLevel 3
DispatcherNoServerHeader 0
DispatcherDeclineRoot 0
DispatcherUseProcessedURL 1
DispatcherPassError 0
</IfModule>
# Define virtual host for brandA.com
<VirtualHost *:80>
ServerName branda.com
DocumentRoot /usr/lib/apache/httpd-2.4.3/htdocs/content/sitea
RewriteEngine on
RewriteRule ^/(.*)\.html$ /content/sitea/$1.html [PT]
<Directory /usr/lib/apache/httpd-2.4.3/htdocs/content/sitea>
<IfModule disp_apache2.c>
SetHandler dispatcher-handler
ModMimeUsePathInfo On
</IfModule>
Options FollowSymLinks
AllowOverride None
</Directory>
</VirtualHost>
# define virtual host for brandB.com
<VirtualHost *:80>
ServerName brandB.com
DocumentRoot /usr/lib/apache/httpd-2.4.3/htdocs/content/siteb
RewriteEngine on
RewriteRule ^/(.*)\.html$ /content/siteb/$1.html [PT]
<Directory /usr/lib/apache/httpd-2.4.3/htdocs/content/siteb>
<IfModule disp_apache2.c>
SetHandler dispatcher-handler
ModMimeUsePathInfo On
</IfModule>
Options FollowSymLinks
AllowOverride None
</Directory>
</VirtualHost>
# document root for web server
DocumentRoot "/usr/lib/apache/httpd-2.4.3/htdocs"
在 Web 服务器重写 URL 时,Dispatcher 需要根据配置 Dispatcher 定义的单个场。需要以下配置才能支持 Web 服务器虚拟主机和 URL 重命名规则:
/virtualhosts
属性必须包含所有 VirtualHost 定义的 ServerName 值。/statfileslevel
属性必须足够高,才会在包含每个域的内容文件的目录中创建 .stat 文件。以下示例配置文件基于随 Dispatcher 一起安装的示例 dispatcher.any
文件。需要进行以下更改才能支持上一个 httpd.conf
文件的 Web 服务器配置:
/virtualhosts
属性促使 Dispatcher 处理 brandA.com
和 brandB.com
域的请求。(第 12 行)/statfileslevel
属性设置为 2,以便在每个包含域的 Web 内容的目录中创建 stat 文件(第 41 行):/statfileslevel "2"
像往常一样,缓存的文档根目录与 Web 服务器的文档根目录相同(第 40 行):/usr/lib/apache/httpd-2.4.3/htdocs
dispatcher.any
/name "testDispatcher"
/farms
{
/dispfarm0
{
/clientheaders
{
"*"
}
/virtualhosts
{
"brandA.com" "brandB.com"
}
/renders
{
/rend01 { /hostname "127.0.0.1" /port "4503" }
}
/filter
{
/0001 { /type "deny" /glob "*" }
/0023 { /type "allow" /glob "*/content*" } # disable this rule to allow mapped content only
/0041 { /type "allow" /glob "* *.css *" } # enable css
/0042 { /type "allow" /glob "* *.gif *" } # enable gifs
/0043 { /type "allow" /glob "* *.ico *" } # enable icos
/0044 { /type "allow" /glob "* *.js *" } # enable javascript
/0045 { /type "allow" /glob "* *.png *" } # enable png
/0046 { /type "allow" /glob "* *.swf *" } # enable flash
/0061 { /type "allow" /glob "POST /content/[.]*.form.html" } # allow POSTs to form selectors under content
/0062 { /type "allow" /glob "* /libs/cq/personalization/*" } # enable personalization
/0081 { /type "deny" /glob "GET *.infinity.json*" }
/0082 { /type "deny" /glob "GET *.tidy.json*" }
/0083 { /type "deny" /glob "GET *.sysview.xml*" }
/0084 { /type "deny" /glob "GET *.docview.json*" }
/0085 { /type "deny" /glob "GET *.docview.xml*" }
/0086 { /type "deny" /glob "GET *.*[0-9].json*" }
/0090 { /type "deny" /glob "* *.query.json*" }
}
/cache
{
/docroot "/usr/lib/apache/httpd-2.4.3/htdocs"
/statfileslevel "2"
/allowAuthorized "0"
/rules
{
/0000 { /glob "*" /type "allow" }
}
/invalidate
{
/0000 { /glob "*" /type "deny" }
/0001 { /glob "*.html" /type "allow" }
}
/allowedClients
{
}
}
/statistics
{
/categories
{
/html { /glob "*.html" }
/others { /glob "*" }
}
}
}
}
由于定义了单个 Dispatcher 场,因此 AEM 发布实例上的 Dispatcher Flush 复制代理不需要特殊配置。
要重写对具有 .html 或 .htm 以外的扩展名的文件的引用,请创建 Sling 重写器转换器组件并将它添加到默认重写器管道中。
当资源路径无法在 Web 服务器上下文中正确解析时重写引用。例如,当图像生成组件创建链接(例如 /content/sitea/en/products.navimage.png)时,需要使用转换器。如何创建功能完善的 Internet 网站的 topnav 组件将创建此类链接。
Sling 重写器是一个后处理 Sling 输出的模块。重写器的 SAX 管道实现由一个生成器、一个或多个转换器和一个序列化器组成:
AEM 使用默认管道重写器来处理 text/html 类型的文档:
htmlparser
。linkchecker
、mobile
、mobiledebug
、contentsync
。linkchecker
转换器将引用的 HTML 或 HTM 文件的路径外部化,以防止链接失效。/libs/cq/config/rewriter/default
节点定义管道。
执行以下任务可创建转换器组件并将它用于管道:
org.apache.sling.rewriter.TransformerFactory
接口。此类创建转换器类的实例。指定 transformer.type
属性的值(转换器别名),并将类配置为 OSGi 服务组件。org.apache.sling.rewriter.Transformer
接口。要最大限度地减少工作量,可以扩展 org.apache.cocoon.xml.sax.AbstractSAXPipe
类。覆盖 startElement 方法可自定义重写行为。为传递给转换器的每个 SAX 事件调用此方法。您可以改为将 TransformerFactory 配置为将转换器插入定义的每个重写器中。因此,您无需配置管道:
pipeline.mode
属性设置为 global
。service.ranking
属性设置为正整数。pipeline.type
属性。使用 multimodule archetype 的内容包 Maven 插件可创建您的 Maven 项目。POM 自动创建并安装内容包。
以下示例实施了重写对图像文件的引用的转换器。
这些示例并不健全,不应在生产环境中使用。
package com.adobe.example;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Property;
import org.apache.sling.rewriter.Transformer;
import org.apache.sling.rewriter.TransformerFactory;
@Component
@Service
public class MyRewriterTransformerFactory implements TransformerFactory {
/* Define the alias */
@Property(value="mytransformer")
static final String PIPELINE_TYPE ="pipeline.type";
public Transformer createTransformer() {
return new MyRewriterTransformer ();
}
}
package com.adobe.example;
import java.io.IOException;
import org.apache.cocoon.xml.sax.AbstractSAXPipe;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.rewriter.ProcessingComponentConfiguration;
import org.apache.sling.rewriter.ProcessingContext;
import org.apache.sling.rewriter.Transformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import javax.servlet.http.HttpServletRequest;
public class MyRewriterTransformer extends AbstractSAXPipe implements Transformer {
private static final Logger log = LoggerFactory.getLogger(MyRewriterTransformer.class);
private SlingHttpServletRequest httpRequest;
/* The element and attribute to act on */
private static final String ATT_NAME = new String("src");
private static final String EL_NAME = new String("img");
public MyRewriterTransformer () {
}
public void dispose() {
}
public void init(ProcessingContext context, ProcessingComponentConfiguration config) throws IOException {
this.httpRequest = context.getRequest();
log.debug("Transforming request {}.", httpRequest.getRequestURI());
}
@Override
public void startElement (String nsUri, String localname, String qname, Attributes atts) throws SAXException {
/* copy the element attributes */
AttributesImpl linkAtts = new AttributesImpl(atts);
/* Only interested in EL_NAME elements */
if(EL_NAME.equalsIgnoreCase(localname)){
/* iterate through the attributes of the element and act only on ATT_NAME attributes */
for (int i=0; i < linkAtts.getLength(); i++) {
if (ATT_NAME.equalsIgnoreCase(linkAtts.getLocalName(i))) {
String path_in_link = linkAtts.getValue(i);
/* use the resource resolver of the http request to reverse-resolve the path */
String mappedPath = httpRequest.getResourceResolver().map(httpRequest, path_in_link);
log.info("Tranformed {} to {}.", path_in_link,mappedPath);
/* update the attribute value */
linkAtts.setValue(i,mappedPath);
}
}
}
/* return updated attributes to super and continue with the transformer chain */
super.startElement(nsUri, localname, qname, linkAtts);
}
}
创建一个 JCR 节点,该节点定义使用您的转换器的管道。以下节点定义创建一个处理 text/html 文件的管道。使用 HTML 的默认 AEM 生成器和解析器。
如果您将 Transformer 属性 pipeline.mode
设置为 global
,则无需配置管道。global
模式将转换器插入所有管道中。
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="https://www.jcp.org/jcr/1.0" xmlns:nt="https://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
contentTypes="[text/html]"
enabled="{Boolean}true"
generatorType="htmlparser"
order="5"
serializerType="htmlwriter"
transformerTypes="[mytransformer]">
</jcr:root>
下图显示了节点的 CRXDE Lite 表示形式: