“All in Cloud”之后,和你聊聊「云原生DevOps的Kubernetes技巧」 了解详情
写点什么

为什么 Segment 会从微服务退回单体架构?

  • 2018 年 7 月 18 日
  • 本文字数:3098 字

    阅读完需:约 10 分钟

近年来,我们发布了很多文章介绍企业向微服务迁移的成败经验。最近, Segment 的 Alexandra Noonan 写了一篇文章,讲述了他们从单体架构迁移到微服务,之后又退回单体应用的经历。文中 Alexandra 具体介绍了他们从原来的简单架构迁移到微服务的过程:

我们原来有个 API 负责拦截事件并将它们转发到一个分布式的消息队列中。这里的一个事件指的是由网页或移动应用生成的,包含用户与用户动作信息的 JSON 对象。当队列中的事件被消费后,系统会检查用户设置来决定接收事件的目标。(……)之后事件被逐个发送到每个目标的 API。这样的流程很合理,因为开发者只需要将事件发送到 Segment 的 API 这一个目标即可,无需创建几十个集成。

如果事件交付失败,就会被系统重新加入队列,也就是说有时工作者进程要一边发送新的事件,一边尝试重新发送之前失败的事件。这样会导致所有目标都出现延迟,Alexandra 解释说:

为解决最紧迫的阻塞问题,团队为每个目标各创建了一个独立的服务和队列。这一新架构包括一个新的路由进程,它会接收入站事件并向每个选定的目标分发一份该事件的拷贝。现在如果某个目标出现问题,只有它自己的队列会回溯,不会影响其它目标。这一微服务化的架构将各个目标独立开来,这样当某个目标又出现常遇到的问题时就会非常有用。

文章之后写到,Segment 的开发团队一开始将所有代码存放在一起,但这引发了许多问题:

最大的麻烦在于,只要一个测试崩溃,那么所有目标的测试都会失败。当我们试图部署一项改动时,我们必须先费劲修复崩溃的测试,就算改动与一开始的变动毫无关联也得这样做。为了解决这个问题,我们决定将代码拆分到每个目标各自的存储库里。

这样一来开发团队的灵活性的确改善了许多。然而随着目标数量的增加,存储库的数量也在同步增长。为了让开发者免受维护这么多代码库的麻烦,Segment 团队创建了许多共享库,存放所有目标通用的变换和功能。这组共享库大大减轻了他们维护工作的压力。但这个措施也有不太容易发现的负面影响:向共享库更新并测试改动会花费大量时间,还会增加破坏无关目标的风险。最后这些库开始分裂为不同的版本,各自不统一,引发了之前没有想到的一个问题:每个目标的代码库都会依赖不同版本的共享库。Alexandra 承认,他们当时可以开发工具来自动将改动更新到这些库中,但那时他们又遇到了这个微服务架构产生的一些新问题。

新出现的问题是,每个服务都有自己的负载模式。有些服务一天处理几个事件,有的服务每秒就能处理几千个。如果目标只处理很少的事件,工作者线程就得在出现负载问题时手动扩展服务以满足需求。

他们的系统集成了自动扩展能力,但因为每个服务都需要指定 CPU 和内存资源分配,调整自动扩展的设置“更像玄学而非科学”。如前所述,每次存储库的数量增长时他们都要增加目标,最后团队平均每月要增加三个目标,当然还要加上更多的队列和服务。

2017 年初, Segment 的一个产品的核心部分使我们达到了峰值负载。当时的情况下我们好像在从微服务的大树上摔下来,一路撞上了所有的树枝。我们这个小团队非但没有提升效率,反而陷入了愈加复杂的泥潭。这个架构的核心优势都成了负担。我们的速度暴降,故障率却在暴增。(……)于是,我们决定回退一步,重新考虑整个流程。

文章最后 Noonan 回顾了他们如何摆脱这个微服务架构,其中他们还开发了 Centrifuge 来替换所有独立的队列,将所有事件都发送到一个单体服务上。他们还将所有目标的代码都迁移到一个存储库里,不过这一次新增了一些代码管理的规则:所有目标都要使用同一个版本,每次更新时同步更替到新版本。他们再也不用操心各个独立版本之间的差异了,因为所有的目标都在使用一个版本,以后也是如此。对于开发者来说,管理越来越多的目标所花费的时间减少了,风险也降低了。

Noonan 的文章还写了很多内容,都是关于他们退回单体服务的经历。感兴趣的读者应该去仔细读一下,文章里面有很多架构细节、关于存储库架构的思考和建立弹性测试集的方法。最后,团队将回退的好处总结如下:

2016 年时我们还在使用微服务架构,我们为共享库带来了 32 项改进。而仅仅今年到现在我们就做出了 46 项改进。过去半年来我们为库带来的改进比 2016 年全年都多。因为所有的目标都处于同一服务内,我们可以很好地搭配 CPU 密集型服务和内存密集型服务,所以扩展服务以满足性能需求变得非常容易。更大的工作者池能负载更多内容,所以我们不再需要将处理少量负载的目标挂起到页面了。

不过这个架构回退过程也有一些负面影响,包括:隔离错误变得更困难(一个目标的错误导致目标崩溃,结果会传染到所有目标);升级一个目标的版本可能会破坏其它一些目标,于是后者也需要升级。Noonan 在文章最后写下了诚恳的总结:

在微服务和单体架构之间做选择时,要注意它们各自都有自己需要考虑的因素。我们的架构中有些部分是微服务表现更出色,但服务端的目标迁移到微服务后的一系列麻烦是一个很好的教训,证明这一流行趋势在某些情况下能对生产力和性能有多大负面影响。结果对于我们来说,单体架构才是最终解决方案。

其实他们关于微服务的某些看法是很眼熟的。今年早些时候我们报道说,ThoughtWorks 根据观察认为微服务尚未进入普及周期。当时的报道写到:“主要原因之一是很多组织并没有为微服务做好准备,他们缺少一些关于运营和自动化的基础实践”。此外, Jan 在另一篇文章中总结了多年来微服务迁移的失败案例。Berico 科技的首席软件工程师 Richard Clayton 提到了他们当时遇到的一个问题:

在不同服务之间共享通用功能代码,以消灭各个服务中的重复功能的努力却带来了巨大的负面影响,最终导致了大规模回退。

回到原文,有很多关于这个话题的讨论,比如 Hacker News Reddit 上的这些;有些讨论者认为与微服务无关的一些因素可能导致了这些问题。比如,有些评论指出 Noonan 的文章并没有引用 CI,只有 CD,起码这是一个奇怪的组合。还有评论认为不止微服务会引发这些问题,所有的分布式系统都是一个样。关于这一点我们之前也提到过,有人使用 SOA 时有过类似的经验:

我曾在一个类似的代码库中工作过,那时他们管它叫 SOA,云还没开始流行。对服务的每次调用都会启动一个完整的服务实例。我想我们应该强制将网络延迟规定为架构设计的要素之一。

有趣的是很多讨论串谈到了微服务中数据上下文的问题。这个话题我们探讨过很多次,这也是微服务反对者的主要论据之一。HackerNews 的一条评论举例说:

比这还糟呢。据我观察多数微服务架构根本就没考虑一致性(“我们才不要乱七八糟的事务!”),盲目地随大流还乐在其中。我搞不懂为啥子人们会觉得,把软件模块拆分开来然后用缓慢不可靠的网络和弱爆的手动连接 REST 处理串起来,就能神奇地让架构面目一新哩?我觉得人们产生这种生产力幻觉的原因是:”我把这些都搞定啦,现在我也有一套’管它是什么即服务‘的先进玩意儿喽!看看那酷毙的数据面板上闪烁的小绿灯吧,我们可是为了它干了好几个月呢!“

另外,为微服务定义域是多年来我们一直强调的微服务部署关键环节。有一篇 PPT 介绍了如何使用 DDD 解构单体应用,Reddit 的一个讨论串也谈到了这一点:

建立一个出色的微服务架构是很难的。我现在觉得关键在于恰当地分隔你的域,当系统进化时持续关注这一层面。微服务并不像它的名字那样,它不必非得那么小,但是要搭配适合这个架构的元素。很多人的失败正是因为忽视了这一点。

其他人怎么看?比如说,Segment 的微服务架构出现的问题能否用其它方式解决,无需退回单体应用?或者一开始的单体架构是否有办法进化得更好,解决原来的问题,而无需切换到微服务?

查看英文原文: Why Segment Returned to a Monolith from Microservices

感谢冬雨对本文的审校。

2018 年 7 月 18 日 06:295035

评论

发布
暂无评论
发现更多内容

Android岗,享学课堂架构师vip

android 程序员 移动开发

云管平台的作用以及应用行业简单介绍

行云管家

云计算 云服务 云平台 混合云 云管平台

BAT面试Java岗经验汇总:面试重点+精选面试120题+6条面试经验!

Java 程序员 面试

Python代码阅读(第45篇):柯里化

Felix

Python 编程 Code Programing 阅读代码

Android事件体系全面总结+实践分析,爆火的Android面试题

android 程序员 移动开发

Android体系化进阶学习图谱,扔物线五期

android 程序员 移动开发

Android基础入门教程,享学课堂Android架构师vip

android 程序员 移动开发

Android外包是如何转正网易的,Android高级工程师面试实战

android 程序员 移动开发

Android学习路线!扔物线朱凯android视频

android 程序员 移动开发

Android之内存泄漏调试学习与总结,面试总结

android 程序员 移动开发

Android初级开发是如何一步步成为高级开发,含答案解析

android 程序员 移动开发

Android大厂技术面试题汇总,享学课堂Android架构师

android 程序员 移动开发

web技术分享| React版本 anyRTC示例对等连接

anyRTC开发者

大前端 音视频 WebRTC React 实时通信

Android工作经验6年,动脑学院vip课程分享

android 程序员 移动开发

Android免打包多渠道统计如何实现,面试突击版

android 程序员 移动开发

Android培训那里好,享学课堂Android架构师vip

android 程序员 移动开发

Android外包是如何转正华为的,音视频开发面试

android 程序员 移动开发

Android大厂面试真题解析大全,10年Android开发经验

android 程序员 移动开发

Android大厂面试真题解析大全,flutter框架

android 程序员 移动开发

Android大厂高级面试题灵魂100问,Android经典面试

android 程序员 移动开发

[TcaplusDB知识库]腾讯云TcaplusDB合理分配存储空间的秘籍

tcaplus

nosql 腾讯云 运维 TcaplusDB 国产数据库

androidwebview设置,享学课堂Android架构师第一期

android 程序员 移动开发

Android岗面试12家大厂成功跳槽,这操作真香

android 程序员 移动开发

android双击事件响应,动脑学院vip视频破解

android 程序员 移动开发

Android学习笔记在互联网上火了,系列教学

android 程序员 移动开发

Android客户端Web页面通用性能优化实践,面试心得体会

android 程序员 移动开发

云计算服务包括哪三种服务?怎么定义?

行云管家

云计算 网络安全 云资源 云管理

Android原生开发如何深入进阶,解析底层原理

android 程序员 移动开发

Android多态实现原理,android开发艺术探索pdf百度网盘

android 程序员 移动开发

Android实习面试经验汇总,温故而知新

android 程序员 移动开发

Android中高级岗面试为何越来越难,细节决定成败

android 程序员 移动开发

西门子低代码:探讨应用程序开发的下一步演进方向

西门子低代码:探讨应用程序开发的下一步演进方向

为什么Segment会从微服务退回单体架构?_DevOps_Mark Little_InfoQ精选文章