OurJS


OurJS-我们的JS, 我们的技术-IT文摘; 专注JS相关领域;
我们热爱编程, 我们热爱技术;我们是高大上, 有品味的码农;

欢迎您订阅我们的技术周刊


我们会向您分享我们精心收集整理的,最新的行业资讯,技术动态,外文翻译,热点文章;
我们使用第三方邮件列表向您推送,我们不保存您的任何个人资料,注重您的隐私,您可以随时退订,

欢迎分享您的观点,经验,技巧,心得

让我们一起找寻程序员的快乐,探索技术, 发现IT人生的乐趣;


本网站使用缓存技术每次加载仅需很小流量, 可在手机中流畅浏览;
如果您发现任何BUG,请即时告知我们: ourjs(at)ourjs.com

微服务运维难维护?数据基础架构公司Segment宣布放弃微服务构架


分享到


分类 大话编程   关键字 分享   发布 ourjs  1557459142150
注意 转载须保留原文链接,译文链接,作者译者等信息。  
很多人可能已经知道微服务已成为明日黄花,它曾经作为最佳实践为Segment公司起到很大作用,但是并不适合所有场所。

简单说,微服务是将后台业务拆分成很多各自功能独立的面向服务软件架构,其模块化、减少测试压力、功能组合、开发团队自治等优点广为人知。与之对应的是单体式架构,即用单个服务为测试部署扩展提供所有功能模块。

2017年早些时候,Segment产品开发遇到了问题。如果在每个部门继续采用微服务,不但不会加速开发过程,反而会落入复杂的泥潭。这种架构的优势反而变成了负担。最终,团队发现需要三个全职工程师才能确保这套系统运转,这种无法承受的负担必须改变。这篇博文就是回顾如何将产品和团队需求更好嵌入开发过程的回顾。


微服务的功过


Segment客户数据架构每秒录入成千上万个时间转发到被我们称为服务端目标的代理商API。有上百种这样的目标,比如Google Analytics,Optimizely或者客户自己的Web钩子。

多年前,产品刚发布时,架构很简单。API录入时间,将他们转发到消息队列。本例中的事件是一个由Web或者移动应用产生的JSON对象,其中包含用户和行为的信息。例如:

  1. {

  2. "type": "identify",

  3. "traits": {

  4. "name": "Alex Noonan",

  5. "email": "anoonan@segment.com",

  6. "company": "Segment",

  7. "title": "Software Engineer"

  8. },

  9. "userId": "97980cfea0067"

  10. }


队列中的时间被处理,客户端配置决定事件会转发到哪个目标端,事件会按照顺序转发到目标端API;这种架构对开发者很有用,他们只需要将事件发送到单一服务端,也就是Segment的API(Segment会接下来出来事件发送到哪个具体目标端),而不需要做其他复杂整合。

如果某个请求失败,一般会过一阵再发送事件请求。某些失败是可以恢复的,但是有些却不能。可重试的错误包括例如HTTP 500s,过快连接和超时等错误;不可重试错误包括无效加密信息或者请求信息不全等状况。


也就是说,单一队列会包含发往所有目标端的最新事件和重试过几次的事件,毫无疑问会引起排头阻塞问题。本例中,如果一个目标端变慢或者失效,重试会阻塞整个队列,从而拖慢所有目标端反应。

假设目标端X出现故障,每个请求都返回超时。请求会产生大量回滚日志,失败请求也会放回重试队列。因为系统架构会根据负载压力弹性扩展,突然增加的队列深度会超过扩展能力从而导致响应减慢。不能及时发送消息,导致等待时间增加对客户是不可接受的。

为了解决排头阻塞问题,我们为每个目标创建了独立的服务和队列。新架构配置了一个额外路由器,负责接收事件并将它们发送到选中的目标端。如果某个目标端出问题,只有与之相关的队列受影响。这种微服务风格的架构将目标端隔离,解决了我们的问题。

各自独立Repos的场景


每个目标端API使用不同请求格式,获得客制化代码,翻译成符合格式的事件。例如payload模块中traits.dob需要目标端X发送生日信息,目标端X的代码如下:

  1. const traits = {}

  2. traits.dob = segmentEvent.birthday


Segment这种方式已经被广为接受。然而,根据目标端API不同这种转换非常复杂。例如,对老式或者无序伸展的目标端,需要更多手工配置XML的压力。

原来,当目标端被分成若干独立服务时,所有代码都存放在一个repo中。如果某个测试失败,就会波及所有代码池相关的应用,需要将每个目标端的代码池分解为各自独立的,随之代码转换也就很自然了。这种分离是的目标端测试和部署也很容易展开。

扩展微服务和Repos


随着业务发展,有50多个新目标端,也就有了50多个新代码池。为了更有效维护代码,创建了共享库,例如HTTP响应库,所有目标端都可以使用它。

例如,某事件需要用户名,event.name()可以从任何目标端代码调用。共享库检查请求字段是否正确和存在,如果不存在,则自动查找first name, firstName,first_name,FirstName等,同样会对last name进行同样操作,然后组合成full name返回。

  1. Identify.prototype.name = function() {

  2. var name = this.proxy('traits.name');

  3. if (typeof name === 'string') {

  4. return trim(name)

  5. }


  6. var firstName = this.firstName();

  7. var lastName = this.lastName();

  8. if (firstName && lastName) {

  9. return trim(firstName + ' ' + lastName)

  10. }

  11. }


共享库使得新目标端创建加速,同样的概念,共享功能使得维护工作大为减轻。

然而,出现了新问题。测试和部署加快影响了共享库的部署。有时为了加速代码部署和测试,不同版本共享库会出现在不同目标端中,造成了共享库的不兼容,共享库带来的红利碰到了瓶颈。同时,微服务架构另外一个问题也开始显露出来。目标端数量不断快速增长(平均每月三个新目标端),带来更多代码池,更多队列和更多微服务,意味着更多的维护问题显现出来,迫使我们停下来反思整个架构。

打通微服务和Queues


第一项工作就是将超过140项服务整合为一个服务。管理这些服务是带来了非常大的工作负载,很多工程师夜里不得不被不断叫醒处理出现的问题。

然而,此时的架构对单一服务体系转向造成很大挑战。独立目标端队列,需要每个队列“工人”检查状态,给每个目标端添加了额外的复杂度。这个挑战催生了“Centrifuge”项目的诞生,它负责替代所有独立的队列,并将事件发送到单一的服务端。


转型Monorepo


因为只有一个服务,可以将目标端代码整合到单一代码池中。尽管实现过程会非常复杂和凌乱。

面对超过120种不同依赖关系,需要最终转变为一种依赖关系,过程中我们不断检查依赖版本,更新到最新版本。结果是大大减少了复杂性,也大大减少了维护人力和成本。

我们还需要解决高效快速运行测试的方法,也就是之前讨论过的场景。幸运的是,目标端测试都有类似的架构。而且验证逻辑也都相仿(执行HTTP请求,验证事件被发送到正确目标端)。之前独立目标端代码池的目标是将测试失败分离开,但是这种“优势”并不能减少失败测试,反而使我们掉入了无法预测的技术泥沼。


弹性测试


测试过程中失败的主要原因就是发往目标端的带外HTTP请求,比如过期认证信息这种无关问题不应该引起失败。而且某些目标端比其他要慢。要解决这些问题,我们创建了“Traffic Recorder”,它运行在yakbak之上,负责记录所有与测试负载相关的内容。当第一次运行测试时,会记录所有请求和响应到一个文件中。后续测试中,会从文件中的请求和响应会回放,而不是发往目标端。这些文件会在repo中登记以保证一致性。通过这种方法,测试变的非常弹性。

我还记得第一次运行整合了“Traffic Recorder”的测试时,瞬间就完成了对140多个目标端的工作,运转如飞,太不可思议了。


为什么单一模式


一旦目标端代码统一为一个代码池,他们就可以被整合为一个单一服务。开发者针对服务中的每个目标端的生产率都提高了。我们不需要部署共享库。

2016年,微服务架构方兴未艾时,通过共享库模式做了32个优化。而现在,我们每年都会有46个优化,甚至过去的六个月的优化比整个2016年都多。

改变也为运维带来好处。因为目标端都在一个服务中,计算力(CPU和内存)使用都得以优化,性能不再是一个问题,运维工程师也不再需要被半夜叫醒解决负载问题。


取舍


从微服务过渡到单一服务对业务效率有很大提升,然而仍然有一些取舍:

  • 容错。大家都运行在一个单体服务中,如果某个bug触发了灾难,会波及所有目标端。我们正在着手解决这个问题。

  • 低效的内存级缓存。之前微服务模式,每个目标端只处理相关访问,内存级缓存信息能够保持命中率;现在内存要处理超过3000个进程,命中率自然会降低。尽管可以用Redis解决这个问题,但已经属于另外一种弹性扩展技术了。目前我们接受这种架构改变带来的效率损失。

  • 升级版本问题。整合为一个服务解决了版本依赖的复杂性,但是也带来了版本升级对稳定服务的影响。这个问题需要用测试套件来解决。目前来看效果还比较理想。


结论


初始阶段我们选择了微服务,通过各自独立解决了性能问题。然而扩展问题并没有解决,尤其是测试部署阶段碰到的复杂性问题,开发团队在这里卡壳了。

转向单一服务模式使得开发效率大大提高,但是我们并没有对这种改变带来的问题掉以轻心。需要将测试套件整合到一个单一代码池,并且避免我们之前碰到的问题。

尽管单一服务也有问题,但是我们做了取舍,而且对这种改变带来的好处比较满意。

刚开始做取舍时,微服务确实对提高开发效率起到推动作用,但是我们的应用场景却证明,单一服务模式更适合。从微服务到单一服务改变要感谢Stephen Mathieson,Rick Branson,Achille Roussel,Tom Holmes,以及团队里的其他人。

特别感谢Rick Branson帮助修改这篇博文。

原文地址: 点此
社区评论 ( Beta版 )
OnceDoc 您自己的企业内容管理系统——文档、流程、知识库、报表、网盘All In One

访问404页面,寻找丢失儿童
 热门文章 - 分享最多
  1. 配置TinyMCE网页文本编辑器不显示html head body等标签信息
  2. Node.JS中用concat和push连接两个或多个数组的性能比较
  3. jQuery用$.prop,$.attr方法来获取或设置checkbox当前选中状态
  4. 判断是否为对象typeof abc == 'object' 与 instanceof 性能比较
  5. JavaScript中将字符串true或false转换成Boolean类型
  6. jQuery用outterHtml获取相对innerHTML父一级包含其自身的html代码内容
  7. 用 OnceAir 搭建个人Git/Svn/照片备份服务器,每年电费7块钱

 相关阅读 - 大话编程
  1. Redis/Python被要求更改Master/Slave程序接口名称和描述
  2. 为jquery的ajax请求添加超时timeout时间
  3. 上海行业工资排名:产品经理一骑绝尘,前端排名第二?
  4. Office365并不是完全基于JavaScript重写的,只是用来构建UI界面
  5. 全国211高校数量最多省市排名:北京、上海、南京、武汉、西安最多,附高考难易地图
  6. IE、Chrome、Firefox浏览器默认首页被改成360导航解决办法(删除daohang88.com)跳转
  7. 马化腾创办腾讯的第一桶金是怎么来的:炒股10万炒到70万
  8. 比特币最近为何会暴跌?大资金如何靠做空比特币获利
  9. 红衣教主周鸿祎会不会成为中国首富
  10. OnceAir顽石企业私有云网盘使用介绍

 关键字 - 分享
  1. 一位自由职业者的分享:程序员怎样找兼职?
  2. node.js函数如何获取调用者的文件目录路径: 用callsite获取错误堆栈的每一层文件名及路径
  3. 华为鸿蒙操作系统想要取代Android几乎不太可能
  4. 我为什么不再用Compass写CSS(缺点分析)
  5. 怎样用纯HTML和CSS更改默认的上传文件按钮样式
  6. 对于现代开发来说,JavaScript就是一种垃圾语言
  7. 树莓派4对比测试:性能提升3倍启动时间反而变慢?TF卡成最大瓶颈
  8. GO有语法缺陷,缺少泛型、public/private、三元运算符?我不喜欢 Go 语言的十个理由
  9. 不用花钱和推广,用户就能从这些渠道进入小程序!
  10. 微服务运维难维护?数据基础架构公司Segment宣布放弃微服务构架

 欢迎订阅 - 技术周刊

我们热爱编程, 我们热爱技术; 我们是高端, 大气, 上档次, 有品味, 时刻需要和国际接轨的码农; 欢迎您订阅我们的技术周刊; 您只需要在右上角输入您的邮箱即可; 我们注重您的隐私,您可以随时退订.
加入我们吧! 让我们一起找寻码农的快乐,探索技术, 发现IT人生的乐趣;


 关注我们

我们的微信公众号: ourjs-com
打开微信扫一扫即可关注我们:
IT文摘-程序员(码农)技术周刊

ourjs官方微信号