软件即服务(SaaS)是一种灵活的软件分发模型,可以由少到一个人或多至上千人的组织来运作。云服务的问世让任何人都可以独立运行自己的SaaS,并在此基础上建立免费增值模式的业务。
与其他类型的软件服务相比,它的系统设计相对简单。但是,由于没有合适的基准架构,如果我们在设计SaaS时没有认真思考,其结果可能会变得一团糟。我见过的一些例子中,SaaS平台最终成为一个庞大却脆弱的单体Web应用程序,其中遍布冗杂的功能。
本文的目的是为你提供一种参考架构,为SaaS实现可伸缩性和可维护性。
将服务隔离到逻辑组件中的设计可以让我们的SaaS有更好的可扩展性。我们应该针对我们的架构作出改进,在规模扩展时打破常规:规模越大,架构却变得越容易管理。就算只有一位孤独的车间开发人员,他也可以很容易运行并管理多个服务,尤其是现在我们已经拥有了各种各样可以编排微服务的云服务,这种事情做起来就更简单了。
因为计划在SaaS平台上运行这些功能Web应用(feature Web app),所以我们应该先思考“我们可以重用什么?”这个问题。
隔离的Web应用程序是将相关功能分组在一起形成的一个Web应用程序,或是代表一款产品的一组Web应用程序。具体而言,我将其称为“产品Web应用”,后文将做更进一步的介绍。
例如,所有"分析报告功能"都可以组合在一起,成为一个独立的Web应用程序,该应用将由一个专门的团队来构建和维护,且这个团队在构建分析报告产品方面具有相关的领域知识。在大型公司中,每个产品Web应用都有自己专门的团队来构建和运行。
你的产品Web应用也可以分为两个部分:内部和公共部分。内部和公共部分之间明确区分开来后,你就能在自己的专用网络内路由一组专用的安全管理工具。
在内部和公共区域代理这些服务的工作由"路由服务"处理,具体将在后面的章节中介绍。
这些是不同的功能Web应用及其相应服务共享的通用功能。每种功能Web应用都需要利用下面列出的这些功能。
下面这些是构成我们SaaS的逻辑组件。
每个请求都需要路由到正确页面。当用户访问yourWebsite.com/your-path时,/your-path需要将用户转发到其关联的请求页面或功能Web应用。路由服务可确保用户得到他们所请求的内容。
在本文的例子中,我们将需要路由服务(routing service)调用另一个服务以获取路径和Web应用程序URL映射,以及所需权限。
这些映射预计将在运行时更新。例如,在需要引入新页面或更改用户权限而无需发布的情况下更新。
路由服务还负责内部或公共的产品Web应用。可以将其部署到两个单独的运行实例中。
公共和私有路由服务设置的高级设计
实现技巧:Node.js的http-proxy-middleware或Nginx的Reverseproxy是两个可行的选择。如果你不需要在运行时从其他服务中以编程方式检索路径、URL和权限,则后者是理想的选择。有关更多信息,请参见后文的"Web应用仓库"章节。
每个产品Web应用都通过一个可在内部访问的URL公开,例如https://my-product-app-1.mydomain.local。其中一些由路由服务路由,以供订阅的用户访问,而另一些仍在内部访问。
通过路由服务路由的产品Web应用
在后台,这可以是单个或一组带有Web前端的微服务。这些Web应用彼此独立,有自己独立的代码库、支持服务、部署管道,并且理想情况下是由不同的团队所有。在SaaS平台中,它们的分组方式与其所有团队的结构保持一致。
这样以来我们就能将大型Web项目分解为许多可管理的单元。可以将这种模式想象成是将大块牛排切成许多小块。一个产品Web应用包含一组不应与其他Web应用重叠的相关功能。
实现技巧:产品Web应用可以是任何Web应用程序,例如常见的Express、Node.js和React堆栈。将设计系统与可复用的微前端(例如Mosaic或Open Components)搭配使用,可以让你的产品拥有一致的外观和体验。
这个服务负责保存多个团队拥有的Web应用程序的记录。路由服务调用Web应用存储库,以获取与所请求路径对应的Web应用URL(匹配某个条目)。
路由服务检索Web应用URL及其关联的路径。
下面是一个Web应用存储库表示例,用来说明它的功能。
基于角色的访问控制(RBAC)服务将提供权限信息,以帮助我们检查登录用户是否有权访问产品Web应用。
如果要维护的产品Web应用条目数量很少,这里就可以用一个简单的路由和硬编码哈希映射的代理。
实现技巧:这既可以是具有自己的Web管理UI的RESTful服务,也可以是简单的基础架构,比如说是位于路由服务中,或者来自对象存储(如AWSS3)的代码配置文件。具体的实现方式将取决于所需的规模和可配置性。如果你要继续考虑服务实现,则可以将Java、Go或C#与RDBMS或NoSQL数据存储搭配使用。
对于这个服务来说,验证过程是非常重要的,如果路径或Web应用URL的配置不正确,则可能会影响你服务的可用性。如果无法确定要支持的产品数量,请使用进化原型方法。你不必在开发周期的早期就构建这个服务,因为一开始只用一个简单的配置文件就够了。
应该有一个跨服务共享的专用身份验证服务。这里JSON Web令牌就可以派上用场,因为我们能轻松地通过标头跨多个服务传递令牌和其他有用的用户信息。
可以从路由服务或产品Web应用中调用身份验证服务。如果从产品Web应用调用身份验证服务,就能让产品Web应用所有者灵活地对他们想要保护的任何页面实施身份验证。这种灵活性让服务所有者可以管理公共页面,例如用户注册页。这种方法还减少了在路由服务中执行的逻辑。
产品Web应用调用身份验证。
或者也可以在路由服务中处理身份验证过程。从安全角度来看,这使身份验证处理更容易审核,因为我们无需查看每一个产品Web应用页面。这也意味着,如果需要让一个网页开启公开访问权限,则应该采用某种形式的白名单。这是另一个可配置性折衷。
路由服务调用身份验证。
实现技巧:AWSCognito、Auth0或任意JSON Web令牌(JWT)身份验证服务。
验证用户身份后,我们需要能够回答以下问题:
可能的答案有:
可能的答案有:
RBAC帮助我们回答上述问题,从而让我们的Web应用可以允许或撤消用户执行的特定动作。
产品Web应用从RBAC服务中检索权限信息。
下图显示了对用户进行身份验证并使用RBAC检查其权限时发生的事件流。
下面的伪代码说明了如何在特定用例中使用RBAC服务。
检查用户是否具有查看页面的权限。
if (userHasPermissionToViewPage(userId)) {
showPage();
} else {
showNoPermissionError();
}
检查用户是否有权执行某个动作。
if (userHasPermissionToPerformAction(userId)) {
showButton();
} else {
doNothing();
}
实现提示:与其他服务相比,这个服务的通信量预计会更高,具体取决于你的权限会有多少粒度。你可以考虑使用和Web应用存储库实现类似的选择:Java、Go或C#,搭配RDBMS数据存储和一个缓存系统。用户权限管理需要一个Web管理UI。
下图显示了运行时中组件之间的交互方式。
这种解决方案的好处包括:明确所有权、重用通用功能以及关注点分离。
下图显示了路由服务将用户路由到请求页面的序列。从Web应用存储库中检索到产品Web应用详细信息之后,便会发生这些序列。
在产品Web应用中调用身份验证。
在产品Web应用中调用身份验证。
在路由服务中调用身份验证。
在路由服务中调用身份验证。
Web应用存储库、RBAC和身份验证服务需要它们自己的Web UI来管理数据或接收用户输入。Web应用存储库、RBAC和身份验证服务均有它们自己的Web应用。使用另一个具有管理UI的Web应用可以管理对某个Web应用的访问权限。
某些Web应用用作其他组件的UI。
Web应用存储库UI允许我们使用所需的权限来管理路径和Web应用URL。
RBAC UI用于管理用户、组织、角色和权限。
身份验证服务的前端UI(即登录页面)也被部署为Web应用。
下面总结了一些应该牢记的准则,可以帮助你成功运营SaaS平台。
关键在于要让团队能通过良好的文档管理产品Web应用,同时充分利用可用的工具。好的文档可以最大程度地减少你的团队重复询问SaaS平台相关问题的需求。
你可以在用于RBAC和Web应用存储库的Web UI管理工具之上,再开发一个NodeJS/React模板,其中包括开箱即用的RBAC和身份验证的客户端,以尽量减少将Web应用添加到SaaS平台所需的工作。
这样你的工程师或你自己就可以方便地从这个模板创建新的Web应用,而无需花费很多精力将其集成到平台中。
糟糕的代码可能会浪费数天甚至数周的开发时间,但相比之下,糟糕的架构决策可能会浪费数月至数年的时间。对于大型公司而言,工程设计审查可能更容易推进;但如果你自己是一名开发人员,那么也可以从社区中认识的其他工程师那里获得反馈。
花费合理的时间预先编写工程设计文档,并在工程设计审查期间获得反馈,可以为你节省很多时间。应该验证软件设计假设,并让其他工程师为你设计的软件架构找出尽可能多的漏洞。
英文原文:
领取专属 10元无门槛券
私享最新 技术干货