微服务走向何方
对微服务的反思
新技术
2015年,我离开微软,加入这家初创公司时,学到的第一件事就是微服务的概念。它被誉为软件开发的未来,承诺提高可扩展性、灵活性和弹性。似乎每个人都在追赶这股潮流,即使是刚刚起步的初创公司,尽管面临着固有的挑战。关于它,有这样一个笑话:
这里有一个一千行的程序,我们必须将其分解为十个一百行的程序。
当我在 2021 年从后端开发领域转向全栈开发领域时,我发现 Next.js、Prisma 和 tRPC 等流行堆栈的所有热门话题似乎都围绕着单片架构,人们不再谈论微服务。
那么,究竟发生了什么?是新趋势、新技术的出现,还是微服务本身在吸取教训后的反思?
我想说两者都有。
对微服务的反思
1.为什么需要微服务?
我们这个行业往往注重技术而非结果。我们应该将微服务作为一种手段,用于实现预期结果,而不是为了使用新技术。
凡事皆有代价,有时人们会忘记追求新技术趋势时需要付出的代价。一些典型的代价包括:
- 增加了开发复杂性
- 基础设施成本呈指数级增长
- 增加组织开销
- 调试挑战
在深入研究微服务之前,务必考虑你希望实现的具体目标。问问自己以下问题:
- 系统内是否存在与系统其他部分以不同速率扩展的东西?
- 系统中是否有某个部分需要比其余部分更频繁的部署?
- 系统中是否存在由单个人或小团队独立于其他成员操作的部分?
一旦您对这些问题有了明确的答案,您就可以进行成本效益分析来确定微服务是否真正对您来说是必要的。
2. 单体应用优先
Martin Fowler 是微服务社区中颇具影响力的人物,但您是否知道他的以下言论:
当我听到有关团队使用 微服务架构的故事时,我注意到了一个常见的模式。
- 几乎所有成功的微服务故事都是从一个变得太大并被分解的整体开始的
- 我听说过的几乎所有从头开始构建的微服务系统最终都陷入了严重的麻烦。
这种模式导致我的许多同事认为你不应该用微服务启动一个新项目,即使你确信你的应用程序足够大,值得这样做。
原因有二:
- 当你开始开发一个新应用时,你有多确定它对你的用户有用?检验一个软件创意是否实用的最佳方法是构建一个简化版本,看看效果如何。在这个第一阶段,你需要优先考虑速度(以及反馈周期),因此微服务的溢价是你应该避免的。
- 只有在服务之间建立良好且稳定的边界,微服务才能良好运行。但即使是经验丰富的架构师,在熟悉的领域工作,一开始也很难确定正确的边界。通过先构建一个单体应用,你可以在微服务设计给这些边界抹上一层糖霜之前,找到正确的边界。
总而言之,在系统早期,没有架构往往是最好的架构。Martin Fowler 的“设计耐力假说”也很好地阐述了这一观点:
3. 单体架构仍可扩展
微服务的倡导者经常认为,单片架构无法在某个点之后有效扩展,但这种观点并不一定正确。
自 2006 年初以来,Shopify 一直以单体应用的形式构建。如今,它已拥有超过 280 万行 Ruby 代码和超过 50 万次提交。2016 年,Shopify 经历了一个转折点,他们意识到构建和测试新功能的挑战日益严峻。
但你应该了解 Shopify 在某个特定时间点的财务状况:
服务 37.7 万商户,实现 3.89 亿收入
此外,他们选择采用 模块化单体架构而非微服务架构。模块化单体架构是指所有代码都支持单个应用程序,并且不同域之间存在严格强制的边界的系统。虽然微服务强调边界的重要性,但边界并非必须由服务定义,也可以由模块实现。这种方法使 Shopify 能够同时享受单体架构和微服务架构的优势,同时最大限度地减少它们各自的缺点。
要了解有关 Shopify 方法的更多信息,您可以阅读他们关于该主题的详细博客文章。
4.分布式系统很难
本质上,微服务是一种构建分布式系统的方式,这意味着它们无法免除此类系统固有的挑战。
最大的障碍之一是跨多个服务执行事务。尽管有多种处理分布式事务的方法,例如两阶段提交协议、补偿事务、事件驱动架构和无冲突复制数据类型,但它们都无法提供开发人员在具有提供事务功能的数据库的单体架构中享受到的那种简便性。当分布式系统出现问题时,可能会出现数据不一致,这或许是开发人员最不想遇到的问题。
新技术
1.无服务器计算
光从名字就能看出,它对微服务来说是一个挑战。😄 但我认为无服务器计算实际上是微服务架构的演进,而非替代。这两种方法的目标都是将单体应用分解成更小、更易于管理的组件。然而,微服务通常需要将每个服务部署到单独的容器或实例中,而无服务器计算则允许开发人员专注于单个功能的代码,而无需担心底层基础设施。毕竟,谁不想享受微服务承诺的所有优势,包括可扩展性、灵活性和弹性,而又不必担心服务器管理或基础设施呢?
尽管它也面临一系列挑战,例如每个功能的执行时间有限以及潜在的供应商锁定,但无服务器计算继续受到欢迎,并被认为是最有前途的新兴技术之一,就像微服务的鼎盛时期一样。
2. 代码编写更少,所需人员更少
不管他们告诉你什么,这始终是一个人的问题。
在弗雷德·布鲁克斯 (Fred Brooks) 的著作《人月神话》(The Mythical Man-Month) 中,他探讨了软件项目的人员数量如何影响项目的可扩展性。布鲁克斯提出了一个著名的观点:“在进度落后的软件项目中增加人力只会使其进度更慢”,这被称为布鲁克斯定律。
微服务可以被视为一种解决方案,通过将大型单体应用拆分成小块来提高可扩展性。每个小块将由一个独立的团队负责,团队成员可以更加自主、更加敏捷地工作。
近期涌现的新框架和工具包在数量和速度上都超出了预期。这些现代工具可以处理越来越多的任务,让开发人员无需像以前那样编写大量代码。例如:
- 使用Next.js,您可以在一个框架中构建整个 Web 应用程序并开箱即用 SSR(服务器端渲染)。
- 使用tRPC,您无需担心使用 RESTful 或 GraqphQL 定义 API。实际初始化 API 调用时,您获得的体验与调用本地函数完全相同。
- 使用Prisma,您可以专注于构建应用程序逻辑,而不是处理数据库查询和迁移。
借助这些工具,小型团队甚至单个开发人员都可以创建高质量、可扩展的应用程序,以处理大量用户和流量。这与过去相比是一个重大转变,过去构建复杂的应用程序通常需要庞大的开发团队。因此,它推迟了团队规模超越单体架构的必然节点。
您可以在另一篇文章中阅读更多内容:
3. 复杂性在于数据层
谈到应用程序的扩展,我们通常会考虑代码的复杂性。然而,实际上,底层数据层往往是扩展问题的根源。传统上,数据层与应用程序代码紧密耦合,随着应用程序的增长,数据实体之间的关系变得越来越复杂,难以管理。
Prisma通过引入模式文件来定义应用程序的数据模型,在降低这种复杂性方面取得了重大进展。
- 模式文件作为数据模型的单一真实来源,使开发人员可以轻松理解和管理应用程序的数据层。
- 模式文件明确指定了数据模型的数据类型、关系和约束,可以随着应用程序的增长轻松修改和扩展。
- 与代码相比,模式可以更好地传达领域的意图和理解。
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
role Role @default(USER)
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
published Boolean @default(false)
title String @db.VarChar(255)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
enum Role {
USER
ADMIN
}
我们在 Prisma 之上构建的工具包ZenStack想要更进一步。我们在架构文件中添加了访问策略层,并自动 为您生成安全防护的前端数据查询库(钩子)、OpenAPI 和tRPC路由器:
model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
role Role @default(USER)
posts Post[]
//everyone can signup, and user profile is also publicly readable
@@allow('create,read', true)
// only the user can update or delete their own profile
@@allow('update,delete', auth() == this)
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
published Boolean @default(false)
title String @db.VarChar(255)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
// author has full access
@@allow('all', auth() == author)
// ADMIN has full access
@@allow('all', auth().role == ADMIN)
// logged-in users can view published posts
@@allow('read', auth() != null && published)
}
enum Role {
USER
ADMIN
}
因此,采用该架构后,后端的大部分工作就是定义架构,它是您业务模型的唯一真实来源。此外,我们正在考虑在架构中实现一些功能,例如支持数据库读写分离。这不仅可以简化扩展,还能在未来达到一定规模时简化将应用程序拆分为微服务的过程。由于架构包含大部分必要信息,它将大大简化向微服务架构过渡的过程。
文章来源:https://dev.to/zenstack/where-did-microservices-go-8m