[本文由 Barry Jones 撰写]
使用微服务架构构建应用程序是一个极好的长期决策,如果您能够负担得起增加的前期投资来正确地完成它。 Heroku 提供了一个大多数开发人员都知道的用于简单部署的平台,但它也极大地简化了微服务架构。
您所说的这些“微服务”是什么?
如果您在微服务一词出现之前接触过 Web 开发,您就会知道它是 面向服务的架构 (SOA) 。最大的区别在于,SOA 更普遍地与基于 WSDL/SOAP/XML 的 Web 服务相关联,这些服务被一般功能、团队和数据访问所隔离,而微服务的一般基调则更深入。微服务还专注于按功能隔离应用程序,但更多地围绕云部署、持续交付、扩展、敏捷开发和减少相互依赖性。
在开发 Web 应用程序时,通常有两种方法来解决问题: Monolith 或 Microservice 。
巨石建造起来要容易得多,速度也要快得多……在一开始。您的整个应用程序可以在单个 IDE 中进行管理。您的所有数据都保存在一个数据库中。您可以交叉查询各种不同的部分来组合报告。您可以通过将相互依赖的插入/更新包装到单个事务中来确保数据完整性,该事务可以在发生故障时回滚。您的控制器可以轻松地从不同的相关模型中提取数据,而无需创建 API。这种类型的开发绝对会导致能够非常快速地推出很多功能。
“从一开始,整体构建起来就更容易也更快了。”通过@codeship
微服务专注于获取功能集并将它们简化为许多更小的应用程序。更小的测试套件、更小的库依赖性、更小的托管要求、更小的代码库、更小的文档、更小的学习曲线以及更容易扩展的各个部分。缺点是您必须开发让各个部分相互通信的方式,通常是通过 REST API 或共享队列,这需要更多时间。此外,您必须关心托管每项服务。
单体与微服务的权衡
让我们看看在选择微服务或单体架构方法时您将经历的一些权衡。
依赖互锁
单体应用的第一个也是最明显的问题是依赖锁。 Ruby gem 和其他遵循能够为自己的功能指定其他 gem 依赖项模式的库最终会产生交叉兼容性问题。
在开发功能时,只需将 gem 添加到 Gemfile 中,进行快速 bundle install ,然后就可以了。随着时间的推移,您会意识到其中一些 gem 具有相同的 gem 依赖项,以及它们的特定版本。当您想升级一个库以获得新功能或改进时,您会发现您做不到,因为其他库的作者更新频率不高,其中一个主干库的版本不兼容。没有另一个就不能升级一个。这涵盖了从次要功能一直到语言版本和框架。
使用微服务架构,问题不太可能发生,因为这些部分将被分成不同的库依赖项集。发生冲突的库有可能甚至不在同一个服务中,但即使它们在同一个服务中,它也会将影响范围缩小到应用程序的一小部分,而不是阻碍整个系统。通过在没有依赖性的情况下重写系统的那部分,完全消除它,或者用更适合其功能的语言替换它,这使得在您的应用程序上实现 水牛理论 变得更加简单。这可能意味着从使用 Go 进行并发到使用 R 进行数据分析的任何事情,但是当您要替换的部件较小时,整个过程会得到简化。
现在可以争辩说,通过为特定功能块创建新服务并让单体应用它来代替原来的服务,单体应用也可以完成同样的事情。如果您有一种简单的方法来验证在任何地方都使用了依赖项,每个地方都使用了被替换的代码,并且删除这些部分并用服务替换它不会影响其他逻辑,那么这种方法是非常好的。
根据我的经验,在编写新服务之前验证所有这些,然后可能将数据迁移到新服务比听起来要难得多……而且听起来已经很难了。
托管挑战
在第一次提交时部署和托管您的单体应用从来都不复杂。随着它越来越大,部署时间越来越长,服务器 RAM 需求增加,启动时间增加,数据库大小增加。那是在我们考虑流量本身增加之前。
即使只是应用程序的一小部分突然增加流量、突发周期或服务器压力……它也会影响整个事情。要扩展它,您必须根据我们到目前为止已经达到的大量服务器要求来扩展整个事情。想要创建一个新的子域来引导某些类型的调用,例如 API 调用,这样您至少可以隔离压力最大的流量?仍然需要为它维护巨型服务器,现在你有额外的负载平衡器,同时将冗余/容量减半,这样你就可以实现隔离。
微服务托管的复杂性往往因所涉及的语言而异。如果一切都用 Java 完成,那么像 JBoss App Server 这样的东西可以将多个独立的应用程序部署为集群中的容器,相互通信,分配负载,甚至分配后台作业。此类设置所涉及的时间投资和复杂性会产生基础架构级别的依赖性和投资偏差。那是因为升级某些服务可能意味着投资一个全新的集群。但在大多数情况下,由于向后兼容,Java 世界使这变得更容易。这么多语言希望在 JVM 上运行是有原因的,而不必使用 Java 本身进行开发就可以访问这些基础设施工具是最大的原因之一。
使用 Go 或 Node.js,通常只是启动的问题,因为服务器是语言的一部分。
使用 PHP 或 Perl,只要使用相同版本的语言,就可以很简单地将数百个服务部署到同一台服务器上。但是,管理资源以独立扩展它们更为复杂。
使用 Ruby,将多个服务部署到同一台服务器会更加复杂且资源密集,除非您利用 Torquebox 之类的东西通过 jRuby 获得 JBoss App Server 的好处。否则,在同一个虚拟机或容器上运行多个生产 Ruby 应用程序会对 RAM 造成相当大的负担。
数据库挑战
围绕微服务架构中的数据库有两种思想流派。一种说法是每个服务都应该有自己的独立数据源。另一个要求多个微服务共享同一个数据库,并允许数据库本身容纳应用程序之间共享的大量逻辑。这些都没有错……这完全取决于您所讨论的应用程序的哪些部分,并且您没有理由不能在它们最适合的地方同时进行。
像 PostgreSQL 这样的数据库使数据库可以做你不知道数据库可以做的事情,并提供几种不同的语言供你编写内部功能(SQL、Javascript(V8)、Python、Perl、R 等……) .那是在您甚至没有考虑您的其他服务可以使用的 PubSub 的 LISTEN/NOTIFY 等工具之前。其他时候,您的数据从互连中获得的收益很少,可能有不同的 I/O 或缓存需求,最好在专门的数据源中独立增长。
最重要的是要避免坚持某种类型的意识形态架构纯度,而这会损害问题的最佳解决方案。隔离在概念上通常更简单,但当您发现自己不断地在服务之间缓存、复制或同步某些数据以减轻压力或加速系统的某些部分时,它会变得更加复杂。
Heroku 是从哪里参与到这一切的……到底是什么?
以下是您在设计微服务架构时脑中的想法:
这是实际发生的事情:
Heroku 通过将理论上通用的基础架构和部署逻辑减少为实践中通用的基础架构和部署逻辑,同时消除了这样做的语言限制,从而为您简化了很多事情。
这些类型的基础设施问题是您在尝试将想法推向市场时不想参与的事情。您专注于您的系统将要做什么、它将如何做、解决它的技术限制以及您的服务需要相互通信的内容。您没有关注的是跨多个服务管理共享配置变量,具有多个环境、多个部署过程、可变数量的服务器,无论您现在是否需要为每个服务打扰负载平衡器,管理 DNS 条目和路由随着 IP 跨实例的变化,或者跨一堆不同的服务子域管理和更新安全证书。
负载均衡器
使用 Heroku 的 Dyno (VM) 设置,您可以随时增加您的 dynos,并按您使用它们的微观时间单位计费。为了通过网络做到这一点,您的测功机位于负载平衡器后面。当你增加或减少它们时,dyno 正在启动,验证成功,检查负载平衡器,然后在它关闭时注销自己。
如果您要设置自己的实例,则必须在它们前面设置自己的负载均衡器,为此做好准备并自动执行注册和注销过程。或者直接指向虚拟机并希望您不需要横向扩展。
SSL 管理
安全证书是那些必要的细节之一,如果您必须自己管理它们,这些细节往往会让人头疼。您的用户将看到的面向公众的 URL,例如您的 API 和您的应用程序前端,绝对需要您获得自己的证书,支付 X 年的费用,在需要更换时更新它,关闭某些类型基于安全漏洞等的加密/握手……在您拥有的每台服务器和负载平衡器上。
在 VPC 中,在公共 URL 后面没有它的风险较小,因为 VPC 正在加密所有内容。但如果没有它,您就需要在整个云基础架构中使用 SSL。每个 Heroku 服务都有一个内部 Heroku 子域。该 URL 具有 Heroku 的 SSL。您不必为它支付额外费用,您不必跟上它,监控它的过期时间,或计划每隔一段时间重新发行它。如果它不是面向公众的,您可以通过使用那些 Heroku URL 来简化整个内部服务流程。
语言特定功能
多语言微服务架构是 Heroku 大获全胜的领域。如果一家公司采用单一语言进行标准化,他们就更容易投入时间开发用于部署、服务器配置、应用程序监控和配置变量的特定于语言的工具。
在许多情况下,配置只会加载到语言文件中或放在服务器上的东西中,并通过 XML、JSON 或 YAML 使用该语言的库进行解析。这造成的最大问题是技术投资。您不仅致力于该语言,而且还致力于围绕它的工具,这对其他语言造成了障碍,这些语言无法访问您已投入时间完善的所有工具。
Heroku 在消除这些投资方面大有作为。对于部署,无论使用何种语言,它都很简单:
git push heroku master
从那里,Heroku 接收到推送,配置 dynos,以正确的方式为每种语言加载库,然后启动 dyno。如果出现问题,无论语言如何,回滚部署都遵循相同的规则。对于滚动重启,您可以简单地打开 预启动 功能,让它启动与您运行的相同数量的测功机。然后等待它们全部成功启动,更换负载均衡器,并关闭旧的测功机。
通过 Heroku Config 工具集中配置。它可以集中更新并自动推送到您所有的测功机。不需要构建一个工具来让他们读取、推送文件,并通过 SSH 以特定语言的重启获取它,将它作为 repo 的一部分包含在语言文件中,或者在你的数据库中管理它并开发逻辑用每种语言来理解它。就应用程序而言,它们是简单的环境变量,无论应用程序语言如何,设置、读取、写入、更改和发布它们都通过单个命令进行管理。
该平台的这些方面减少了您对语言的投资,甚至减少了您在垂直扩展服务器配置(阅读“企业应用程序服务器”)时可能遇到的语言版本限制。可以在每项服务的基础上轻松完成升级。回滚是一致的。配置一致。部署是一致的。整体式应用只有一套,所以这些通常不是问题。微服务架构必须在每个服务的基础上考虑它们,而 Heroku 消除了大部分这些决定。
消除技术投资(除了学习它)可以让您更轻松地为手头的工作选择合适的工具。
- 利用 Rails 快速开发具有所有外围细节的全功能 Web 应用程序,以顺利处理 HTML 和资产。
- 使用 Go 来处理高并发、低 I/O 工作负载,例如发送电子邮件、发送 webhook 或限制和传递 API 请求。
- 使用 R 进行数据分析。
- 将 Java 用于有其他语言无法使用的库的地方,或者因为您的团队中有“用 Java 做所有事情”的人(开玩笑……主要是)。
你明白了。通过打开现有的所有技术大门,这将成为您整个系统的推动者。
日志和监控
无论 Linux 版本、应用程序语言或运行的服务数量如何,Heroku 都会将一项服务的所有日志聚合到一个地方。您可以在运行 50 个测功机的电子邮件服务上打开日志尾部,并在一个地方实时查看每个服务器。您可以与第三方服务集成以收集、解析和分析您的日志,甚至可以设置您自己的 日志外流 以自行处理它们。
Heroku 处理实际的 dyno 监控和响应,这样就不用担心了。在运行的应用程序中进行监控的工具仍然是特定于语言的,但 New Relic 的免费计划使这更易于管理(Heroku 或其他)。
开发/暂存环境
微服务架构中的另一个主要因素是您必须为开发、UAT 或暂存环境复制生产设置。与在虚拟机上复制整个堆栈相比,Heroku 的免费 dyno 计划使这成为一个微不足道的问题。只需创建环境并更改配置……完成。
开发人员访问
当您开始构建系统时,您的团队的访问控制通常不会出现在您的脑海中。充其量,您担心限制对生产环境的访问。使用 Heroku,整个过程变得更容易,因为您可以将特定人员添加到特定项目。您可以授予他们访问应用程序的权限以执行诸如运行作业之类的事情,而无需提供对服务器的 SSH 访问权限,因为服务器可能暴露的内容超出您的预期。
用户数量不花任何钱。 Heroku 为他们提供了一个界面来管理他们自己的 RSA/DSA 密钥和密码,而无需将它们分发到不同的服务器集。所有这些“嘿,我可以访问……”请求只是在您希望允许它们的特定环境中单击几下即可。
总结
像 Docker 这样的容器技术在这个领域带来了更多的竞争 ,但它 并不完美 。 Docker 显示出足够的前景, Heroku 现在甚至支持它 ,而一些 主要的市场参与者正在集中资源 来打磨该技术的粗糙边缘。有一天,他们可能会像 Heroku 目前所做的那样让这一切变得简单,但时间会证明一切。
单个微服务非常适合 持续集成/交付工作流 ,因为较小的部分可以更轻松地部署,而不会中断整个系统。利用这些流程在构建成功完成后 自动部署到开发/暂存/uat 环境, 创造了另一个很好的机会来为您的时间之轮加油。
Heroku 消除了使用微服务架构进行开发所涉及的大部分前期投资。它使得处理几乎无限数量的片段变得相当微不足道,这正是您以一种让您保持理智的方式跟踪微服务所需要的。如果没有专门的 IT 人员,使用不止一种语言的微服务会产生很多 DevOps 问题,而 Heroku 可以缓解其中的绝大多数问题,因此您可以专注于您的应用程序。
当使用 Heroku 时,这改变了整体与微服务问题之间的决策公式。单体在短期内提供的许多优势与微服务处于更平等的地位,这应该使关注长期成为更容易的选择。
每个应用程序和业务要求都会有所不同。哪个最适合您的设置?在微服务架构还是单体架构之间做出决策时,您有哪些顾虑?