当多个团队要将代码部署到同一AEM环境时,他们应遵循一些实践,以确保团队能够尽可能独立地工作,而无需与其他团队紧密协作。 尽管这些技术永远无法完全消除,但这些技术将最大限度地减少跨团队的依赖关系。 为了使并行开发模型成功,开发团队之间的良好沟通至关重要。
此外,当多个开发团队在同一AEM环境中工作时,可能会有一定程度的多租户。 关于尝试在AEM环境中支持多个租户的实际考虑,特别是在管理治理、运营和开发时面临的挑战方面,已经写了很多文章。 本文探讨了在多租户环境中实施AEM的一些技术挑战,但其中许多建议将适用于具有多个开发团队的任何组织。
请务必事先注意,尽管AEM可以支持在单个环境中部署多个网站甚至多个品牌,但它不提供真正的多租户。 某些环境配置和系统资源将始终在环境中部署的所有站点之间共享。 本白皮书为最大限度地减少这些共享资源的影响提供了指导,并为简化这些领域的沟通和协作提供了建议。
实施多租户环境存在许多挑战。
其中包括:
尽管面临挑战,但运行多租户应用程序确实有好处,例如:
如果业务需要真正的多租户,而对其他租户一无所知,且没有共享的代码、内容或常见作者,则单独的创作实例是唯一可行的选项。 应将总体发展努力的增加与基础架构和许可证成本的节约进行比较,以确定这种方法是否最合适。
在管理Maven项目依赖项时,所有团队都必须在服务器上使用相同版本的给定OSGi包。 为了说明在Maven项目管理不当时会出现什么问题,我们举了一个示例:
项目A取决于库foo的1.0版本;foo版本1.0已嵌入到其到服务器的部署中。 项目B取决于库foo的1.1版;foo版本1.1已嵌入到其部署中。
此外,我们还假定此库中的API在版本1.0和1.1之间发生了更改。此时,这两个项目中的一个将不再正常运行。
为了解决这一问题,我们建议将所有Maven项目设为一个父级反应堆项目的子项。 此反应堆项目有两个用途:如果需要,它允许将所有项目一起构建和部署,并且它包含所有子项目的依赖关系声明。 父项目定义依赖项及其版本,而子项目仅声明它们所需的依赖项,从而继承父项目的版本。
在此情景中,如果处理项目B的团队需要在版本1.1 foo中使用功能,则开发环境中会很快发现,此更改将破坏项目A。此时,团队可以讨论此更改,并让项目A与新版本兼容,或为项目B寻找替代解决方案。
请注意,这并没有消除这些团队共享这种依赖关系的必要性,它只是快速、及早地突出了问题,以便团队可以讨论任何风险并就解决方案达成一致。
处理多个项目时,务必确保代码不重复。 代码复制会增加出现缺陷的可能性、系统更改的成本以及代码库中的整体刚性。 为防止重复,请将通用逻辑重构为可在多个项目中使用的可重用库。
为了支持这一需求,我们建议开发和维护一个所有团队都可以依赖和贡献的核心项目。 在这样做时,必须确保此核心项目不再取决于各个团队的任何项目;这允许在保持独立可部署性的同时,仍提升代码重复使用。
通常在核心模块中使用的一些代码示例包括:
模块化项目架构
这并不意味着需要多个团队依赖并可能更新同一组代码。 通过创建核心项目,我们减少了团队之间共享的代码库大小,但减少了对共享资源的需求。
为确保对此核心包所做的更改不会中断系统的功能,我们建议由高级开发人员或开发人员团队负责监督。 一种选择是,由一个团队来管理此包的所有更改;另一种方法是让团队提交拉取请求,并由这些资源审核和合并。 必须由团队设计并同意治理模型,并且开发人员应遵循该模型。
由于不同的团队将代码部署到同一存储库,因此请务必不要覆盖彼此的更改。 AEM具有一种机制,可在部署内容包(过滤器)时控制这种情况。 xml文件。 筛选器之间不存在重叠,这一点很重要。 xml文件,否则一个团队的部署可能会清除另一个团队之前的部署。 要说明这一点,请参阅精心编制的过滤器文件与有问题的过滤器文件的以下示例:
/apps/my-company与/apps/my-company/my-site
/etc/clientlibs/my-company与/etc/clientlibs/my-company/my-site
/etc/designs/my-company与/etc/designs/mycompany/my-site
如果每个团队明确地将其过滤器文件配置到他们正在处理的站点,则每个团队都可以单独部署其组件、客户端库和站点设计,而不会清除彼此的更改。
由于它是全局系统路径,并且不是特定于一个站点,因此核心项目中应包含以下Servlet,因为此处所做的更改可能会潜在地影响任何团队:
/apps/sling/servlet/errorhandler
叠加图通常用于扩展或替换现成的AEM功能,但使用叠加图会影响整个AEM应用程序(即,任何叠加功能更改都可供所有租户使用)。 如果租户对叠加层的要求不同,则情况会更加复杂。 理想情况下,业务组应合作,就AEM管理控制台的功能和外观达成一致意见。
如果各业务部门之间无法达成共识,则可能的解决方案就是不使用叠加图。 相反,应创建功能的自定义副本,并通过每个租户的不同路径显示该功能。 这允许每个租户具有完全不同的用户体验,但这种方法也会增加实施和后续升级工作的成本。
AEM使用工作流启动器在存储库中进行指定的更改时自动触发工作流执行。 AEM提供了一些开箱即用的启动器,例如,用于对新资产和更新资产执行演绎版生成和元数据提取流程。 虽然可以按原样保留这些启动器,但在多租户环境中,如果租户具有不同的启动器和/或工作流模型要求,则可能需要为每个租户创建和维护单个启动器。 需要将这些启动器配置为在其租户的更新中执行,同时保留其他租户的内容。 通过将启动器应用到特定于租户的指定存储库路径,可轻松完成此操作。
AEM提供了可以按页面设置的虚URL功能。 在多租户场景中,此方法的问题在于AEM无法确保以这种方式配置的虚URL之间的唯一性。 如果两个不同的用户为不同的页面配置相同的虚路径,则可能会遇到意外行为。 因此,我们建议在Apache Dispatcher实例中使用mod_rewrite规则,该规则允许具有与仅出站资源解析程序规则相一致的中央配置点。
为多个创作组开发组件和模板时,务必要有效地使用componentGroup和allowedPaths属性。 通过将这些组件与网站设计有效结合使用,我们可以确保品牌A的作者只查看为其网站创建的组件和模板,而品牌B的作者只查看他们的组件和模板。
良好的架构和开放的通信渠道有助于防止在网站的意外区域引入缺陷,但这些方法并非万无一失。 因此,在将任何内容发布到生产环境之前,务必要充分测试平台上部署的内容。 这要求各个团队在其发布周期上进行协调,并且强化了对一套自动化测试(可尽可能多地覆盖功能)的需求。 此外,由于一个系统由多个团队共享,因此性能、安全性和负载测试变得比以往更加重要。
AEM在单个JVM内运行;任何已部署的AEM应用程序都会在正常运行AEM时已消耗的资源之上内在地彼此共享资源。 在JVM空间本身中,线程之间没有逻辑分离,AEM可用的有限资源(如内存、CPU和磁盘I/O)也会共享。 任何消耗资源的租户都会不可避免地影响其他系统租户。
如果不遵循AEM的最佳实践,则开发的应用程序可能会消耗超出正常范围的资源。 例如,触发了许多繁重的工作流操作(如DAM更新资产)、在多个节点上使用MSM修改推送操作,或者使用昂贵的JCR查询来实时渲染内容。 这些操作将不可避免地对其他租户应用程序的性能产生影响。
AEM提供开箱即用的界面,以便进行强大的日志记录器配置,以便在共享开发场景中发挥我们的优势。 通过为每个品牌指定单独的日志记录器,按包名称,可以实现一定程度的日志分离。 虽然复制和身份验证等全系统操作仍将记录到中心位置,但非共享的自定义代码可以单独记录,从而简化了每个品牌技术团队的监控和调试工作。
由于JCR存储库的性质,传统备份在整个存储库中工作,而不是在单个内容路径上。 因此,不能根据每个租户轻松划分备份。 相反,从备份中恢复将回退系统上所有租户的内容和存储库节点。 虽然可以使用VLT等工具执行目标内容备份,或通过在单独的环境中构建包来选择要恢复的内容,但这些
方法不会轻松包含配置设置或应用程序逻辑,并且可能难以管理。
当然,可以使用访问控制列表(ACL)来控制谁有权根据内容路径查看、创建和删除内容,这需要创建和管理用户组。 维护ACL和组的困难取决于如何强调确保每个租户对其他租户一无所知,以及部署的应用程序是否依赖共享资源。 为了确保高效的ACL、用户和组管理,我们建议设立一个具有必要监督的集中组,以确保这些访问控制和承担者重叠(或不重叠),从而提高效率和安全性。