我在微服务项目中工作的经验和学习
在过去的几年中,微服务已经成为我们行业中非常热门的话题,并被视为构建更加解耦、可扩展且易于维护的应用程序的推荐方法。
大约一年前,我参与了一个从零开始采用微服务架构的项目。
在这篇文章中,我将分享我的经验和心得。
项目
为了让您了解一些背景信息,我所从事的项目是构建一个类似于Skyscanner 的航班预订网络应用程序。
主用户流程非常简单。用户会看到一个搜索表单,可以输入想去的地方、日期等等。
他点击搜索按钮后,会显示可用航班的列表。
然后,用户可以选择其中一个航班,并开始结账流程,输入个人信息,然后选择付款方式。旅程结束后,用户将收到航班确认邮件。
开始——确定您的服务边界
构建微服务架构的第一步是明确服务边界。思考你的领域模型以及领域实体之间的关系。微软的这份指南是一个很好的资源,它更详细地解释了这个主题。
这一点非常重要。如果服务边界定义不明确,最终可能会导致服务之间耦合过度。如有疑问,建议先从较大的边界开始。这样以后再拆分成更小的单元会比反过来更容易。
从我上面描述的主要用户流程中,我们可以清晰地识别出一些潜在的服务。搜索服务,负责查询航班提供商并显示可用航班列表。“订单”服务,负责管理预订/结账流程。支付服务,负责处理所有与支付相关的事务,甚至还有消息服务,负责向用户发送确认邮件。
这种关注点分离在业务领域方面很有意义,但您也可以看到在可扩展性和操作方面的优势。
您的搜索量将远高于预订量,因此搜索服务可能需要比订单服务扩展得更大。将其作为单独的服务可以实现这一点。
此外,搜索服务需要进行大量处理才能从多个合作伙伴处获取可用航班。作为一项独立的服务,我们可以选择最合适的语言和工具来完成这项工作。此外,不同的团队可以分别处理这些服务。
这就是我们最终建造的。
航班业务确实很复杂,所以我们最终将搜索服务进一步分解为多个协同工作的更小的专业服务。
我们还提供了一些其他较小的服务,我就不详细介绍了。
总的来说,这个结构非常成功,但当然也有一些不太积极的地方,我接下来会谈到。
一个不该诞生的状态机服务
在公司内部,我们发现不同项目之间存在一个共同的需求:每个项目都需要某种状态机。这个项目也不例外。我们需要一个用于订单和支付的状态机。
因此,我们决定开发一个通用的状态机服务,以便公司其他所有项目都能使用。虽然理论上听起来很棒,但实际操作起来却不太理想。让我来解释一下原因。
第一个问题是“通用”状态机这个术语。泛化和抽象当然通常是一个好主意,但它往往会导致过度工程,而且比做一些更具体的事情要复杂得多,也更耗时。
为了能够正确地概括和抽象解决方案,必须清楚地了解所有业务需求以及它们最终将如何随着时间的推移而发展以及服务的每个潜在用户的特定需求。
事实并非如此。我们花了更多时间去预测所有可能的功能和需求,而不是专注于根据从这个特定项目中获得的新信息构建更具体的产品。
第二个问题是,如果你思考一下我们的领域模型以及我之前提到的服务边界,你会发现单独的状态机并没有多大意义。它无法独立工作,必须与某个业务实体关联,在本例中就是订单或支付。它显然属于订单/支付边界。
这两个服务之间的紧密耦合显而易见,因为最终每个操作都会在单个请求中多次调用对方,并产生所有相关的开销。
在目前的实现中,订单服务离不开状态机服务,状态机服务离不开订单服务。由于两个服务都是同步通信,这个问题变得更加严重,这就留给了本项目的下一个学习内容:尽可能优先使用异步通信。
如果我们想重用一些代码,我们可以构建一个库和一些 SDK,而不是创建一个完全独立的服务。
更喜欢异步通信
虽然每个服务都应该是一个独立的可部署单元,但它们协同工作以构建您的应用程序,因此它们之间当然存在一些依赖关系,并且必须以某种方式进行通信。但这种依赖关系不必是硬依赖,即如果某个服务宕机,就会导致整个应用程序宕机。
如何避免这种情况?通过使用消息队列等异步通信。
该项目中的一个例子是支付服务。当用户完成预订时,订单服务会向支付服务发起同步 HTTP 调用以完成支付。之后,支付服务会向外部支付提供商发起另一个请求,以有效地完成支付。
这个过程很慢,而且中间有很多事情可能会失败。使用异步通信可以使您的服务更灵活地应对故障,因为您可以更轻松地实现诸如发生错误时重试之类的机制。
结论
微服务架构的优势显而易见,它可以更好地分离关注点,从而实现更加解耦的应用程序,并且可以独立扩展每个服务或将每个服务编写成最合适的编程语言。
但微服务并非万灵药。应用程序的编排/部署更加复杂,调试更加困难,并且每个服务与其他服务的通信方式也需要深思熟虑。
如果要遵循这条路线,你需要很好地规划你的架构。根据领域模型正确识别服务边界是成功实现微服务的第一步。
由于我所负责项目的性质,服务边界定义清晰,采用微服务架构完全合理。如果边界不太清晰,或者预计会发生很大变化,那么一开始就采用更单体的架构,之后再根据需要提取部分功能也完全没问题。
“单体”这个词通常带有贬义,但事实上,没有什么能阻止你拥有一个模块化、关注点分离、低耦合、高内聚的单体应用。像 SOLID 这样的流行设计模式可以帮上大忙。如果你能做到这一点,那么随着应用程序的增长,将应用程序的特定部分迁移到单独的服务中就会相对容易。
每个应用程序都各不相同。作为软件工程师,你的工作就是分析需求并选择最合适的路径。
希望你喜欢这篇文章。如果你有任何问题或意见,欢迎在下方评论区留言。