我们为什么停用微服务?
作者|David翻译|平川策划|万佳
在Botify , 我们工程团队的核心价值观之一是ownership 。 我们赋予工程和产品团队自主权以及灵活性 , 让它们拥有自己的项目并完成这些项目 。 然而 , 随着我们在更大的技术栈上工作 , 团队规模也越来越大 , 我们开始遇到一些关于如何共享工作成果的问题 。
2016年年底 , 我们想赋予工程师和产品经理更多的localownership , 从而能快速轻松地将他们的产品和技术栈投入使用 。 为此 , 我们决定将Django应用程序拆分为微服务 。
这个故事阐述了我们是怎样失败 , 以及如今我们为何又将这些服务迁回到单体应用 。 同时 , 我们还会花些时间来分享我们的经验教训 。
在深入讨论前 , 我想强调下:本文的目的并非指责微服务 。 在Botify , 我们的理念是“使用最合适的工具”——我们认为 , 微服务并非此刻解决我们问题的适当工具 。
开启微服务之旅首先 , 简单介绍下Botify的历史 。 Botify于2012年在Python/Django技术栈上创建 。 到2016年初 , 整个Botify平台都是通过我们Django应用程序的负载均衡集群提供服务 。 当时 , 我们有一个大约15人的产品和工程团队 。
为什么选择微服务?以下两个目标促使我们评估在技术栈中使用微服务的可能性:
提高开发速度:我们希望减少前端和后端之间的依赖关系 , 并增加localownership , 从而将开发主体限定为一个小团队;
统一技术:我们在巴黎招聘Python/Django工程师越来越多困难 , 所以我们就想 , 前后端都统一使用JavaScript会让招聘工作更容易 , 因为我们在转向“全栈”JavaScript这样一个单一的角色 。
梦想要大 , 但要从小事做起我们决定从身份验证和授权栈入手实现第一个微服务 。 我们认为 , 应该从小处着手 , 将Django应用程序当前的一部分功能转移到微服务中 。 我们创建了一个小型的JavaScript团队 , 负责实现NodeJS后端 , 用于处理用户及其帐户和权限 。 我们称之为CustomerSuccess 。

文章图片
使用微服务的BotifyAPI架构概要痛点组织当你像我们一样 , 在人为因素的驱动下做出技术决策时 , 你很快就会遇到问题 。 新团队的组织和流程很难与现有的团队并行不悖 。 由于特性是独立开发的 , 很快就出现了知识分化 。 微服务内部的东西没法共享 , 而跨栈代码审查的缺失让我们觉得失去了ownership 。
隔离你可能已经注意到 , 在上面的架构中 , 微服务CustomerSuccess并非完全隔离 。 从单体到微服务存在后端到后端的通信 , 反之亦然 。 这并不完全是坏事(似乎还很常见) , 但这还是会降低性能 , 并在两个服务之间创建依赖关系 。 从长远来看 , 这还意味着更复杂的机制 , 如circuitbreakers和优雅的服务降级(为保证正常运行时间和可用性) 。
我们还看到 , 在该模式下 , 我们主要的关系数据库是共享的 。 在数据库内部 , 一些表被映射到Django和Express/Sequelize的模型 。 换句话说 , 修改共享表的模式需要对微服务和单体进行同步修改 。 这是由糟糕的领域隔离所导致的 。
工具随着时间的推移 , 我们学会了如何健壮地构建和部署我们的Django应用程序 , 但是 , 对于每个新的技术栈 , 我们必须重新掌握这个过程 。 虽然在微服务环境中 , 独立部署是一个核心优势 , 但与部署微服务相比 , 我们还是对部署单体更有信心 。 不过 , 我们花了比较少的时间就为我们的微服务实现了健壮、流畅的自动化部署 。
在日常工作中 , 我们遇到的问题越来越多 , 为了让架构更有效 , 我们需要不断地修改解决方案 。 期间 , 我们针对Django技术栈做了一些工作 , 改进代码、覆盖率、可测试性、依赖关系和性能 。
我们颠覆了最初驱使我们采用微服务的模式 。
微服务技术栈是从当前流行的框架和开发团队最熟悉的框架中选取的 。 然而 , 在Botify , 我们坚信 , 要为合适的工作选择合适的工具:合适的工具不一定是最著名的工具 , 也不一定是我们已经知道的工具 。
我们确定 , 是时候认真地(重新)考虑我们的微服务架构和CustomerSuccess后端了 。
去而复返 , 重回单体鉴于每天都要在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单体的主要原因 。
停用微服务停用微服务非常简单 , 有许多方法可供选择 。
我们希望在终止CustomerSuccess后端时 , 所作的更改尽可能少 , 这意味着要模拟微服务的行为 , 在单体中逐个重写API端点并切换路由 。 这样 , 更改只会影响API , 而不影响前端 , 从而避免可怕的同步部署或API版本控制 。
以下是我们停用微服务所采取的步骤:
识别路由:列出微服务提供的所有API路由 , 确定它们的用途和目标 。 先处理简单的情况:有些路由可能没用 , 或者没有必要 。
在目标后端创建路由:最麻烦的工作是在目标代码库中编写相同的逻辑 。 相同的输入 , 相同的输出 , 还要最小化切换风险 。
Y分支(Y-branch)调用 , 比较结果:设置一个简单的分支系统 , 使用两个后端并比较响应 , 以发现不一致的地方 。
根据QA反馈 , 进行修复:QA大量而广泛地审查逻辑 , 以发现任何遗留的Bug 。
逐步扩大Y分支范围 , 以使用新迁移的逻辑 。
待所有逻辑都迁移到单体后 , 删除微服务 , 并完成清理工作 。 移除正在运行的实例和计算机 。
我们可以很自豪地说 , 我们在2月4日10:34成功地停用了NodeJS身份验证后端CustomerSuccess , 没有任何同步部署或API版本控制 。

文章图片
停止时间:10:34如前所述 , 停用后端非常简单 , 使用这套方法 , 我们没有遇到大的麻烦 。
【我们为什么停用微服务?】实际上 , 我们遇到的最令人沮丧的问题是我们自己找出的:在构建CustomerSuccess后端时 , 我们选择的策略是 , 不管REST调用的结果是什么 , 总是返回200响应 , 并使用布尔型的success和响应关键字statusCode提供更具体的错误信息:
{"errors":[{"message":"Organizationwasnotfound","code":404,"name":"NotFoundError
推荐阅读
- 科学探索|为什么科学会在需要时让我们失望?
- 不起眼的朗姐|和老人出去旅游,为什么会觉得心累?网友:只要老妈身体允许以后还要带她去,哈哈哈哈
- 粤游记|旅游就该诗酒趁年华,带你一起到东京,我们玩点不一样的!
- 终于|十四年后,那句“不是你撞的为什么要扶”, 终于酿成了恶果
- 聚会|小沈阳同学聚会照,现实告诉我们:有明星的聚会还是不要去了
- 历史真相:印度为什么要年复一年谋求“入常”,有哪些国家支持?
- 中华冷兵器课堂|斩马刀为什么被大家称为“古兵神器”?它有什么妙处?
- 有父母的家,才是我们温暖的家。
- 苦要自己尝|宝宝为什么更爱吃妈妈做的饭?“妈妈的味道”竟藏着大的秘密
- 村民|有的离任村干部,为什么比普通村民还清贫?原因就是这3个!
