10月21日,杭州云栖大会-技术&可持续发展论坛,注册有礼 了解详情
写点什么

我们为什么停用微服务?

2020 年 5 月 29 日

我们为什么停用微服务?


在 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 年 5 月 29 日 07:0019023
用户头像

发布了 385 篇内容, 共 170.6 次阅读, 收获喜欢 918 次。

关注

评论 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
回复
没有更多了
发现更多内容

堡垒机品牌就认行云管家!为什么呢?

行云管家

云计算 系统运维 堡垒机 IT运维 云计算运维

多张图片的形式

冇先生

webrtc AlrDetector

糖米唐爹

机会!痛点!难点!中国游戏泛娱乐企业出海攻略全解析

环信

游戏出海 直播 社交APP出海 泛娱乐社交

多线程、分布式、高并发都不懂?你拿什么跳槽?

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

centos8 mediasoup 搭建

糖米唐爹

WebRTC mediasoup

底层即真理!Netty+Redis+ZooKeeper解读高并发架构

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

工具 | 使用 CLion 编译调试 MySQL 8.0

RadonDB开源社区

MySQL 数据库

FNDZ构建的交易复制生态,让DeFi回归价值本身

股市老人币圈新

云时代的到来会淘汰运维人员吗?运维工作可以一直做吗?

行云管家

云计算 运维 云服务 IT运维 云时代

浅谈消息中间件,MQ的来龙去脉

Java技术那些事

Java 编程 程序员 计算机 8月日更

去哪儿网数据同步平台技术演进与实践

Qunar技术沙龙

数据库 数据中台 ES 数据同步 Kafk

银行小程序隐私安全如何做?诊疗一体,一步到位

WeTest

IM开发技术学习:揭秘微信朋友圈这种信息推流背后的系统设计

JackJiang

即时通讯 IM 微信朋友圈

基于KubeEdge实现中国移动10086客服云边协同平台

华为云原生团队

云计算 开源 运维 边缘计算 边缘技术

EMQ 映云科技成为开源项目 Vue.js 定期捐赠者

EMQ映云科技

Java 开源 前端 emq

给Arm生态添把火,腾讯Kona JDK Arm架构优化实践

腾源会

开源 腾讯 jdk 腾讯开源 KonaJDK

“区块链”赋能智慧社区,多维度提升管理质效

旺链科技

区块链 智慧社区

业界良心啊!第五次更新的Spring Cloud Alibaba升级太多内容

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

太为难我了,阿里面试了7轮(5年经验,拿下P7岗offer)

云流

Java 编程 程序员 架构 面试

二本渣渣5面阿里,从准备简历到“直怼”面试官,经历了什么?

云流

Java 程序员 架构 面试 计算机

千亿级模型在离线一致性保障方案详解

百度Geek说

百度 测试 后端

腾讯WeTest零售行业质量解决方案

WeTest

阿里这份15w字Java核心面试笔记!GitHub凭借百万下载量位居榜首

Java~~~

Java 架构 面试 微服务 多线程

架构师训练营-毕业设计

俞立夫

云原生 | 混沌工程工具 ChaosBlade Operator Node 篇

RadonDB开源社区

数据库 云原生 混沌工程

架构实战营毕业总结

俞立夫

架构实战营

Fil今日价格行情?Fil未来多少钱一枚?

区块链 分布式存储 IPFS fil价格 fil行情

linux工具之TC

糖米唐爹

好评如潮,PerfDog两年迭代正式启动商业化探索

WeTest

终于有人把TCP协议与UDP协议给搞明白了

编程菌

Java 编程 程序员 计算机 java技术宅

数据cool谈(第1期)数据库寻路,开源有态度

数据cool谈(第1期)数据库寻路,开源有态度

我们为什么停用微服务?-InfoQ