事实上,我真的很喜欢巨石。但我是那种希望我的工具箱里装满工具的工程师,即使是那些只在非常特殊的情况下才有用的看起来怪异的工具。关于维护技术,我通常给人们的建议是接受Conway定律的必然性,并在架构决策中考虑团队结构、组成和规模。小团队倾向于构建巨型团队,因为小团队往往是巨型团队。
与基于服务的架构相比,单体架构还可提供稍好的性能。至少一开始是这样。只有当产品达到规模经济时,能够单独构建、部署和扩展系统的不同组件所带来的好处才会超过在可以使用库的情况下进行网络呼叫所增加的延迟。当然,当是时候进行这种转变时,组织已经达到了增长的水平,其主要焦点是竞争,竞争,以及更多的竞争。打破单调,同时推出新功能,就像是一边重塑你的房子,一边举行晚宴。对于一家小公司来说,新功能和新销售几乎总能胜出。一旦一家小公司变成了一家成熟的公司,拆分巨石的任务就分散到了多个团队中,并增加了复杂性。
在起义军,我们有另一个理由有时倾向于更铁板一块的方法。政府网络可能很复杂,对云不友好。这可能意味着在一个私有的、可能有“气隙”的、带有边缘设备的网络上运行,在那里我们可以重现类似云的体验,而无需连接到传统的云环境。易于耦合或解耦的体系结构可以提供优势。
所有Rebellion产品都建立在一个称为服务核心(Servicecore)的框架之上。Servicecore的核心是GRPC服务器,然后内置到servicecore中的是工程师构建良好应用程序所需的许多工具。当工程师使用服务核心时,他们得到了免费的监控,他们得到了免费的跟踪,他们得到了适当的日志记录和健康检查,ORM集成,而且迁移也更容易了。他们还获得了可以根据需要启用但默认关闭的功能,例如Rebellion客户以自助方式管理访问控制角色的界面。但是这些选项本身都是使用servicecore框架构建的,这意味着它们可以作为单独的服务运行,也可以组合成一个紧密耦合的整体应用程序,而不需要真正的代码更改。
已经有很多关于设计应用程序以促进微服务的文章。例如,面向领域的体系结构将类似的功能组织在一起,从而最终可以拆分较大的服务。但是这个领域的大多数建议并不能帮助您轻松地重新配置代码。拆分服务是很难的,如果由于某种原因,拆分成了一个错误,那么将它们重新组合在一起就更难了。您必须围绕功能可以逻辑连接的位置设计解决方案,才能拥有这种级别的灵活性。
当构建一个可以自行拆分的整体时,第一个问题是两组功能应该如何耦合?最基本的方法是将代码放在同一位置,然后当应用程序扩展到维护两个不同服务有意义的大小时,必须将代码移到单独的存储库中并重新构建,以便彼此独立运行。
另一种解决方案是将一个服务实现为包并由主机服务导入。这使每个域的代码保持分离,并意味着多个服务可以重用相同的部分,但通常需要宿主服务构建与包逻辑交互的端点和接口。一旦我们需要数据库或前端,导入包就不那么方便了。
但是,通过构建允许这些元素在其自己的服务器上运行或注入到另一服务器的框架,我们可以根据需要耦合和解耦功能集。
服务核心是一个应用程序对象(…。这就是说,它实际上是一个结构,但对象是大多数程序员更熟悉的术语)。希望在servicecore之上构建服务的工程师只需在应用程序对象中指定他们想要的配置,并将其传递到servicecore。Servicecore遍历所有应用程序对象,收集并排序配置,创建GRPC服务器,并将完整配置传递给该服务器。
即使是最基本的服务,也有多个应用程序对象,因为使服务对开发人员更有价值的工具本身也都是应用程序对象。因此,servicecore使以两种不同的方式将服务耦合在一起成为可能:我们可以使用应用程序对象将端点及其所有逻辑和依赖项附加到同一服务器,从而有效地扩展它。或者应用对象可以注入中间件来改变服务器端点的功能。
诚然,我是在中间件是体系结构模式恶棍的时期作为一名软件工程师磨练出来的。中间件是“软件粘合剂”,在错误的人手中可能会以一种对调试它的软件开发人员隐藏的方式改变函数的行为。但是现代的Web服务有很多重叠的需求。每次都必须为每个请求执行日志记录和身份验证等行为。谁愿意仅仅因为其他工程师可能滥用一个好工具就把它扔出工具箱呢?我记得在使用框架时,为了避免中间件诅咒,架构师希望您使用样板代码,以透明的方式在每个端点的顶部调用这些函数。是否删除检查用户是否已登录的行?我猜这个页面现在已经公开了。
添加中间件的机会分为两类。它们要么在函数之前执行(前挂钩),要么在函数之后执行(后挂钩)。我们可以通过跟踪典型请求通过应用程序的路径来确定像servicecore这样的框架可以公开的一些选项。无论特定端点应该执行什么操作,请求都会经过以下阶段:
每个端点都必须接收请求,确认发送请求的对象有权这样做,执行与端点相关的功能(可能会触发对其他服务或数据库的查询),并交付响应。
因为我们使用的是GRPC,所以我们有一种内置的方式,可以通过拦截器连接到传输和响应阶段。即使我们在流式传输数据,我们也可以附加在请求数据传递到实际端点之前执行的逻辑。事实证明,这是实现授权阶段的好方法。它还可以用于添加日志记录,以便于调试。
然后是与服务本身的生命周期相关的挂钩。我们绝对希望允许在主机服务和扩展之间传递配置。我们使用Cobra为我们的服务提供命令行界面,因此servicecore可以包括钩子,以便在初始化Cobra时或在服务器运行时添加命令。
这些连接点为我们提供了很大的灵活性,可以在不增加工程团队开销的情况下向产品添加功能,方法是要求他们在每个服务的群集中维护代码和不断增长的服务器机队。分布式系统的复杂性最终是有价值的,但前提是产品本身的使用级别已经扩大到足以证明它是合理的。在此之前,运行服务解耦只是增加小型团队工作负荷的另一组职责。
Servicecore做的一件事就是执行约定。通过创建打包配置并为您创建GRPC服务器的框架,我们可以添加验证逻辑,以确保事物的命名方式和行为方式的某些方面是一致的。
但是构建这样的框架的另一面是,它可能被用来产生难以理解和调试的臃肿的服务。一个大问题是应用程序加载到服务核心的顺序。重复项将被忽略,挂钩将按顺序执行。改变这一顺序可能会以不是所有工程师都明显的方式影响结果。中间件的潜在问题并没有消失。
Servicecore之所以被称为servicecore,是因为它旨在作为服务的核心,而不是完整的产品,但如果您能够将一组功能的所有权交给一到两个人的团队,而不会给他们带来单独的监控、随叫随到的轮换和部署的负担,那么维护产品也更容易。
我们现在是一个小团队,但我们成长得很快。看看我们的空缺职位。