【QCon】精华内容上线92%,全面覆盖“人工智能+”的典型案例!>>> 了解详情
写点什么

我们为什么停用微服务?

  • 2020-05-29
  • 本文字数:3937 字

    阅读完需:约 13 分钟

我们为什么停用微服务?


在 Botify,我们工程团队的核心价值观之一是 ownership。我们赋予工程和产品团队自主权以及灵活性,让它们拥有自己的项目并完成这些项目。然而,随着我们在更大的技术栈上工作,团队规模也越来越大,我们开始遇到一些关于如何共享工作成果的问题。


2016 年年底,我们想赋予工程师和产品经理更多的 local ownership,从而能快速轻松地将他们的产品和技术栈投入使用。为此,我们决定将 Django 应用程序拆分为微服务。


这个故事阐述了我们是怎样失败,以及如今我们为何又将这些服务迁回到单体应用。同时,我们还会花些时间来分享我们的经验教训。


在深入讨论前,我想强调下:本文的目的并非指责微服务。在 Botify,我们的理念是“使用最合适的工具”——我们认为,微服务并非此刻解决我们问题的适当工具。

开启微服务之旅

首先,简单介绍下 Botify 的历史。Botify 于 2012 年在 Python/Django 技术栈上创建。到 2016 年初,整个 Botify 平台都是通过我们 Django 应用程序的负载均衡集群提供服务。当时,我们有一个大约 15 人的产品和工程团队。

为什么选择微服务?

以下两个目标促使我们评估在技术栈中使用微服务的可能性:


  1. 提高开发速度:我们希望减少前端和后端之间的依赖关系,并增加 local ownership,从而将开发主体限定为一个小团队;

  2. 统一技术:我们在巴黎招聘 Python/Django 工程师越来越多困难,所以我们就想,前后端都统一使用 JavaScript 会让招聘工作更容易,因为我们在转向“全栈”JavaScript 这样一个单一的角色。

梦想要大,但要从小事做起

我们决定从身份验证和授权栈入手实现第一个微服务。我们认为,应该从小处着手,将 Django 应用程序当前的一部分功能转移到微服务中。我们创建了一个小型的 JavaScript 团队,负责实现 NodeJS 后端,用于处理用户及其帐户和权限。我们称之为 Customer Success。



使用微服务的 Botify API 架构概要

痛点

组织

当你像我们一样,在人为因素的驱动下做出技术决策时,你很快就会遇到问题。新团队的组织和流程很难与现有的团队并行不悖。由于特性是独立开发的,很快就出现了知识分化。微服务内部的东西没法共享,而跨栈代码审查的缺失让我们觉得失去了 ownership。

隔离

你可能已经注意到,在上面的架构中,微服务 Customer Success 并非完全隔离。从单体到微服务存在后端到后端的通信,反之亦然。这并不完全是坏事(似乎还很常见),但这还是会降低性能,并在两个服务之间创建依赖关系。从长远来看,这还意味着更复杂的机制,如 circuit breakers 和优雅的服务降级(为保证正常运行时间和可用性)。


我们还看到,在该模式下,我们主要的关系数据库是共享的。在数据库内部,一些表被映射到 Django 和 Express/Sequelize 的模型。换句话说,修改共享表的模式需要对微服务和单体进行同步修改。这是由糟糕的领域隔离所导致的。

工具

随着时间的推移,我们学会了如何健壮地构建和部署我们的 Django 应用程序,但是,对于每个新的技术栈,我们必须重新掌握这个过程。虽然在微服务环境中,独立部署是一个核心优势,但与部署微服务相比,我们还是对部署单体更有信心。不过,我们花了比较少的时间就为我们的微服务实现了健壮、流畅的自动化部署。


在日常工作中,我们遇到的问题越来越多,为了让架构更有效,我们需要不断地修改解决方案。期间,我们针对 Django 技术栈做了一些工作,改进代码、覆盖率、可测试性、依赖关系和性能。


我们颠覆了最初驱使我们采用微服务的模式


微服务技术栈是从当前流行的框架和开发团队最熟悉的框架中选取的。然而,在 Botify,我们坚信,要为合适的工作选择合适的工具:合适的工具不一定是最著名的工具,也不一定是我们已经知道的工具。


我们确定,是时候认真地(重新)考虑我们的微服务架构和 Customer Success 后端了。

去而复返,重回单体

鉴于每天都要在 JavaScript 身份验证后端和 Django 模块之间频繁地来回切换,我们把工程师们召集在一起,权衡该架构的优缺点以及潜在的迁移成本。


我们做出大胆选择,将身份验证后端重新加入到 Django 单体中,并重新生成了我们在 2016 年从 Django 转换到 Node 的 API 端点。为什么要重新采用以前的做法?下面是其中一些原因。

为恰当的问题选择恰当的方案

重要的是要记住我们的用例以及 Botify 为谁服务。我们的平台是一个企业级 B2B 服务,帮助 Web 上最大的网站改善他们的 SEO。我们的主要挑战在于对客户数据的分析,而不是我们的流量:我们每个月要处理 PB 级的数据,在一些长期运行的任务上,比如爬取或处理数据。


不过,我们不需要巨大的面向用户的流量,因为在撰写本文时,我们的用户主要是 Web 上大型站点的 SEO 经理。处理用户相关数据的微服务架构旨在服务于高流量的 B2C 平台,而 Botify 的挑战在于动态地聚合数以 GB 的 SEO 数据,让其在几秒钟内可用。对大约一万名客户的元数据以毫秒为单位进行响应,这项任务无需高度可伸缩的微服务架构。恰恰相反,我们的后端到后端通信减慢了这些简单的检索过程,花费了更多时间。

共享资源意味着同步部署

举个例子,最近的一个项目是删除一个无人维护的依赖项,该依赖项会将 JSON 数据库列作为文本处理,以便利用 PostgreSQL 中内置的列类型 jsonb。


然而,这些文本列是在两个代码库之间共享的,逐步迁移非常麻烦。同步部署多个后端很容易出错,尤其是在规模大并存在负载均衡的情况下,这与微服务最初的好处背道而驰。在 Botify,我们总是喜欢用这个警示性的故事来说明我们不喜欢同步部署。这是个有趣的故事,如果你有时间的话,可以一读。

分离的代码库淡化了知识、ownership 和联系

微服务最初由少数人实现,是为了保护新来者不受无人维护的代码库的影响,同样的人处理了所有的演进。这种现象违背了我们的代码评审理念:任何人都能够评审并理解正在发生的事情。在 Botify,我们喜欢开放式的工作方法,这样,团队中的任何人都可以查看所有代码并发表评论或提出建议。使用单独的技术栈、团队和语言,这让我们失去了团队合作和严格评审给开发团队带来的许多好处。


以上这些促使我们停用身份验证微服务并将其端点迁回 Django 单体的主要原因。

停用微服务

停用微服务非常简单,有许多方法可供选择。


我们希望在终止 Customer Success 后端时,所作的更改尽可能少,这意味着要模拟微服务的行为,在单体中逐个重写 API 端点并切换路由。这样,更改只会影响 API,而不影响前端,从而避免可怕的同步部署或 API 版本控制。


以下是我们停用微服务所采取的步骤:


  • 识别路由:列出微服务提供的所有 API 路由,确定它们的用途和目标。先处理简单的情况:有些路由可能没用,或者没有必要。

  • 在目标后端创建路由:最麻烦的工作是在目标代码库中编写相同的逻辑。相同的输入,相同的输出,还要最小化切换风险。

  • Y 分支(Y-branch)调用,比较结果:设置一个简单的分支系统,使用两个后端并比较响应,以发现不一致的地方。

  • 根据 QA 反馈,进行修复:QA 大量而广泛地审查逻辑,以发现任何遗留的 Bug。

  • 逐步扩大 Y 分支范围,以使用新迁移的逻辑。

  • 待所有逻辑都迁移到单体后,删除微服务,并完成清理工作。移除正在运行的实例和计算机。


我们可以很自豪地说,我们在 2 月 4 日 10:34 成功地停用了 NodeJS 身份验证后端 Customer Success,没有任何同步部署或 API 版本控制。



停止时间:10:34

痛点

如前所述,停用后端非常简单,使用这套方法,我们没有遇到大的麻烦。


实际上,我们遇到的最令人沮丧的问题是我们自己找出的:在构建 Customer Success 后端时,我们选择的策略是,不管 REST 调用的结果是什么,总是返回 200 响应,并使用布尔型的 success 和响应关键字 statusCode 提供更具体的错误信息:


{  "errors": [    {      "message": "Organization was not found",      "code": 404,      "name": "NotFoundError"    },  ],  "success": false,  "statusCode": 404}
复制代码


虽然这是一种常见的 REST 实践,但它不适用于 Django REST 框架。在不更改契约的情况下,将这些端点从 Express 迁移到 Django,需要大量重写 Django REST 框架的行为,才能匹配我们的 Express 实现。我们没有利用 HTTP 状态码来处理错误,而是对其进行了修改,这样,我们就可以用相同的响应关键字返回 200 状态码。令人失望,但也不是太糟糕。这种差异让人心里发痒,如果我们认为有必要,就会把它作为技术债务的一部分加以修正。好的一面是,我们的微服务定义了一种智能的跨源资源共享策略,这个必须要复制到我们的单体系统中。虽然一开始做正确的事情很麻烦,但它使我们能更好地保护应用程序免受恶意攻击。

小结

本文不是要批评微服务,也不是要对 Django 和 Express REST 后端评短论长。每一种技术都自有其用途,但我们相信,要在正确的时间选择正确的解决方案来构建我们的产品。如今,我们不必来回切换,也不用维护两个后端,显然,我们已经从中获益。


顺便提一下,当我们构建微服务后端时,我们实际上还同时构建了另一个后端,这个后端直到今天都还用着。它的功能与用户以及我们的单体应用联系比较少,所以它运行地很好,依赖和痛点也都更少。如果我们必须再次实现一个类似的特性,我们可能会在单体应用中实现,但是现在,既然它还没出问题,我们就先不修复了。


对于某些用例和情况,我们仍然相信微服务。我们并不排除未来创建新的微服务的可能性。不管怎样,我们已经从错误中吸取了教训。


我们的团队在组织结构上也有了变化: 现在实行小组制,有单独的小组负责产品特性以及前端和后端工程师面临的技术挑战,还有一名产品经理和一名 QA 工程师。我们吸取教训,每个小组都几乎是独立地设计和实现其特性,但代码库在小组间是共享的,代码审查则是由 Botify 的所有工程师负责。这让我们可以共享 ownership、知识和技术栈维护,并达到我们作为一个团队希望达到的卓越技术水平。


英文原文:


To Kill a Microservice


2020-05-29 07:0020245
用户头像

发布了 683 篇内容, 共 390.9 次阅读, 收获喜欢 1498 次。

关注

评论 9 条评论

发布
用户头像
全文看下来,只能说你们技术难以支撑你们推进微服务
2020-06-27 21:53
回复
用户头像
他們缺乏一位有能力的軟體架構師,感覺是個 newbie 在帶 team. 微服務不是只有 DDD 做完就上,數據也得拆分, 通過一些手段像 CQRS 去做橫向數據、timer 等元件的擴展,這還只是基礎,看起來他們對其下的 Infra 結構也沒什麼著墨,多半是只會套framework的 junior engineer, 分散不了就把People/Process/Technology 拿出來數落一遍,再扯個 Culture 就下課了。

他們真的只能走 monolithic program.
2020-06-07 05:04
回复
用户头像
“不管 REST 调用的结果是什么,总是返回 200 响应”,失败也返回200,为什么你们会有这种奇妙的操作……
2020-06-07 00:16
回复
用户头像
技术太烂!微服务应该是分离数据库的
2020-06-06 20:27
回复
用户头像
我觉得更想是没有选对合适的NodeJS微服务框架,建议作者看一下moleculer
2020-06-06 19:29
回复
用户头像
拆分的微服务技术栈不同、而且共享数据库影响到了部署上线,这种场景确实不适合
2020-06-04 10:27
回复
用户头像
没怎么看懂啊, 他为啥不用微服务啊?
2020-06-01 11:01
回复
他沒說的:
就是沒有做數據拆分,不懂使用 CQRS, 沒有搞清楚 Data ownership, 讓每個人都當 owner,結果就是在數據庫打架。
2020-06-07 05:23
回复
因为设计和技术能力太差
2020-06-08 09:03
回复
没有更多了
发现更多内容

JS基础——JS数据类型

龙之幽谷

前端 js

Eclipse Theia技术揭秘——自定义布局

龙之幽谷

开发工具

架构实战营模块1作业

乖乖IvyShine

医疗卫生机构应该多久进行一次等保测评?

行云管家

网络安全 信息安全 等保测评 等级测评

MySQL查询数据库表记录数

源字节1号

VSCode技术揭秘(一)

龙之幽谷

vscode 开发工具

软件测试 | 测试开发 | 一种能有效缓解环境噪声对音频质量干扰的方案

测吧(北京)科技有限公司

测试

AntDB数据库与鼎甲科技完成产品互认证,共筑数据安全防线

亚信AntDB数据库

AntDB AntDB数据库

AntDB入选《爱分析:2022数据智能厂商全景报告》

亚信AntDB数据库

AntDB AntDB数据库

软件测试 | 测试开发 | Spring boot 之 RestTemplate访问

测吧(北京)科技有限公司

测试

软件测试 | 测试开发 | gitlab 服务端 hook, 拦截糟糕的提交到仓库

测吧(北京)科技有限公司

测试

双非二本程序员,年近30,5年间在大厂中横跳,工资翻了三番

程序知音

Java java面试 后端技术 秋招 Java面试八股文

软件测试 | 测试开发 | Shell 进程通过 ContentProvider 实现跨进程通信

测吧(北京)科技有限公司

测试

边缘计算在视频直播场景的应用与实践

火山引擎边缘云

边缘计算 视频直播 火山引擎边缘计算

企业级npm仓库搭建

龙之幽谷

前端 nexus NPM仓库

leetcode 572. Subtree of Another Tree 另一棵树的子树 (简单)

okokabcd

LeetCode 数据结构与算法

运维学网络安全还是云计算好?哪个更有前景?

行云管家

云计算 运维 网络安全

软件测试 | 测试开发 | InfluxDB 2.0 原理与应用实践

测吧(北京)科技有限公司

测试

软件测试 | 测试开发 | 利用 rpush 和 blpop 实现 Redis 消息队列

测吧(北京)科技有限公司

测试

软件测试 | 测试开发 | Pytorch GPU 训练环境搭建

测吧(北京)科技有限公司

测试

元年洞察|数字化转型进程中的创新技术菜谱

元年技术洞察

数据中台 数字化转型 趋势研究

仅靠一文便火爆全网!开源阿里绝密800页JDK源码笔记:霸榜GitHub

Geek_0c76c3

Java 数据库 程序员 架构 开发

Eclipse Theia技术揭秘——脚手架源码分析

龙之幽谷

开发工具

软件测试 | 测试开发 | 自定义form表单验证

测吧(北京)科技有限公司

测试

软件测试 | 测试开发 | Golang死信队列的使用

测吧(北京)科技有限公司

测试

用 nodejs 搭建脚手架

coder2028

node.js

javascript 高级编程 之 Array 用法总结

hellocoder2029

Vue

Eclipse Theia技术揭秘——构建桌面IDE

龙之幽谷

开发工具

认识 ESP-IDF-v4.3+工程结构(ESP32-C3应用调整示例)

矜辰所致

ESP32-C3 9月月更 ESP-IDF

Forrester发布中国数据治理生态报告,亚信科技AntDB数据库等四款数智产品入选

亚信AntDB数据库

AntDB AntDB数据库

数据中台打造企业数据能力组件中心

元年技术洞察

数据中台 SaaS服务应用 PaaS平台化能力

我们为什么停用微服务?_架构_David Wobrock_InfoQ精选文章