转自:Medium开发团队谈架构设计

 背景

  说到底,Medium是个社交网络,人们可以在这里分享有意思的故事和想法。据统计,目前累积的用户阅读时间已经超过14亿分钟,合两千六百年。

  我们支持着每个月两千五百万的读者以及每周数以万计的文章发布。我们不想Medium的文章以阅读量为成功的依据,而是观点取胜。在Medium,文章的观点比作者的名头更重要。在这里,对话促进想法,并且很看重文字的力量。

  我是Medium开发团队的负责人,此前在Google工作,负责开发Google+和Gmail,还创立了Closure项目。业余时间我喜欢滑雪跳伞和丛林冒险。

  团队介绍

  说起团队我非常自豪,这是一群富有好奇心而且想法丰富的天才,大家凑到一块是想做大事的。

  团队以跨功能的任务驱动,这样每个人既可以专攻,又可以毫无压力的对整个架构有所贡献。我们的理念就是接触的方面越多,对团队的锻炼越大。更多关于团队的理念见此

  在工作组织方面,我们有着很大的自由度,当然作为一个公司组成,我们还是有季度目标的,并且鼓励敏捷开发模式。我们使用GitHub进行code review和问题跟踪,用Google Apps作为邮件、文档和表单系统。跟很多团队习惯使用Trello不同,我们是Slack和slack机器人的重度用户。

  原始架构

  最开始的时候,Medium部署在EC2上,用Node.js实现,后来公测的时候迁移到了DynamoDB

  其中有个节点用来处理图片,负责将复杂的处理工作转向GraphicsMagick。还有一个节点用作后台的SQS队列处理。

  我们用SES处理邮件,S3做静态元素服务器,CloudFront做CDN,nginx作为反向代理,Datadog用来监控,Pagerduty用来告警。

  在线编辑器用了TinyMCE。上线之前我们已经开始使用Closure编译器以及部分的Closure库,但是模板还是用的Handlebars

  当前架构

  虽然Medium表面看起来很简单,但是了解其后台的复杂性后,你会大吃一惊。有人会说,这就是个博客啊,用Rails之类的一周就能搞定了。

  总之,闲话不多说,我们自底向上介绍以后再做判断。

  运行环境

  Medium目前运行在Amazon虚拟私有云,使用Ansible做系统管理,它支持配置文件模式,我们将文件纳入代码版本管理,这样就可以随时回滚随时掌控。

  Medium的后台是个面向服务的架构,运行了大概二十几个产品服务。划分服务的依据取决于这部分功能的独立性,以及对资源的使用特性。

  Medium的主体仍然是Node.js完成,方便前端和后端的代码共享,主要是文章编辑和发布这个过程。Node大部分时候不错,但阻塞event循环的时候会有性能问题。为了缓解,我们在每台机器上启动多个Node实例,将对性能要求比较高的任务分配给专门的实例。同时我们还深入V8运行时环境查看更加细节的耗时,基本上是JSON去串行化的时候的对象具体化耗时较多。

  我们还用Go语言做了一些辅助服务。因为Go非常容易编译打包和发布。相比Java语言的冗长罗嗦和虚拟机,Go语言在类型安全方面做的很到位。就个人习惯来讲,我比较喜欢在团队内部推广强类型语言,因为这类语言能够提高项目的清晰度,不纠结。

  目前静态元素大部分是通过CloudFlare提供的,还有5%通过Fastly,5%通过CloudFront,这么做是为了让两者的缓存得到更新,用于一些紧急的情况。最近我们在应用流量上也使用了CloudFlare,当时主要是为了防止DDOS攻击,但随之而来的性能提升也是我们愿意看到的。

  我们使用Nginx和HAProxy做反向代理和负载均衡,来满足我们所需功能的维恩图。

  我们仍然使用Datadog来监控,Pagerduty来告警。现在又增加了ELK(ElasticsearchLogstashKibana)来进行产品问题调试。

  数据库

  DynamoDB仍然是我们的主力数据库,但是用起来也不是毫无问题。目前遇到的比较棘手的是大V用户展开和虚拟event过程中的热键问题。我们专门在数据库前面做了一个Redis缓存集群,来缓解这些问题。到底为开发者优化还是为产品稳定性优化的问题通常会引发争执,我们也一直在尝试中和两者的矛盾。

  目前我们开始在存储新数据上使用Amazon Aurora,它可以提供更灵活的查询和过滤功能。

  我们使用Neo4J存储Medium网络中实体之间的关系,运行在有两个副本的主节点上。用户、文章、标签和收藏都属于图中的节点。边则是在实体创建和用户进行推荐高亮等动作时生成。我们通过在图中游走来过滤和推荐文章。

  数据平台

  早期我们对数据非常渴望,不断尝试数据分析框架来辅助商业和产品决策。最近我们则是利用同样的框架来反馈产品系统,支持Explore等数据驱动功能。

  我们采用Amazon Redshift作为数据仓库,为生产工具提供可变存储和处理系统。我们持续将诸如用户和文章等核心数据从Dynamo导入Redshift,还将诸如文章被浏览被滚动等event日志从S3导入Redshift。

  任务通过一个内部调度和监控工具Conduit调度。我们用了一个基于断言的调度模型,只有条件满足的时候,任务才会执行。从产品角度来讲,这是不可或缺的:数据制造方应该与数据消费方隔离,还要简化配置,保持系统的可预见和可调试性。

  Redshift的SQL检索目前运行不错,但我们时不时需要读取和存储数据,所以后期增加了Apache Spark作为ETL,Spark具有很好的灵活性和扩展能力。随着产品的推进,估计后面Spark会成为我们数据流水线的主要工具。

  我们使用Protocol Buffers作为schema来确保分布式系统的各层次间保持同步,包括移动应用、web服务和数据仓库等。通过定制化的选项,我们将schema标记上更加细化的配置,如带有表名和索引,以及长度等校验约束。

  用户也需要保持同步,这样移动端和网页端就可以保持日志的一致性了,同时方便产品科学家们用同样的方式解析字段。我们帮助项目成员从.proto文件中生成消息、字段和文档等内容,进而利用所得数据开展研究。

  图片服务器

  我们的图片服务器现在用Go语言实现,采用瀑布型策略来提供处理过的图片。服务器使用groupcache,是memcahce的替代品,可以帮助减轻服务器之间的重复工作。而内存级缓存则是用了一个S3的持续缓存。图片的处理是请求来触发的。这给了我们的架构设计师灵活改变图片展示的自由度,为不同平台优化,而且避免了大量的生成不同尺寸图片的操作。

  目前Medium对图片主要支持放缩和裁剪,但原始版本中还支持颜色清洗和锐化等操作。处理动图很痛苦,具体后续可以写一篇文章来解释。

  文本标注

  文本标注是个有意思的功能,用了一个小型Go服务器,跟PhantomJS接口形成渲染进程。

  我一直想要把渲染进程换到Pango,但是在实践过程中,能在HTML中摆放图片的能力的确更灵活。而从功能的使用频率来看,这意味着更容易开发和管控。

  自定义域名

  我们允许用户为其Medium文章设置个性化域名。我们想做成单点登录且HTTPS全覆盖,因此实现起来颇有难度。我们专门准备了一批HAProxy服务器用来管理证书,并向主要应用服务器引导流量。初始化一个域的时候需要一些手动的工作,但是通过与Namecheap的定制化整合,我们将其大部分转换为自动化。证书验证和发布链接由专门服务负责。

  网站前端

  网页端这块,我们有自主研发的单网页应用框架,使用Closure标准库。我们使用Closure模板渲染客户端和服务端,然后使用Closure编译器来缩减代码并划分模块。编辑器是我们网页端应用最复杂的部分,具体参见Nick此前的文章。

  iOS

  我们的两个应用都是原生的,尽量避免使用网页视图。

  在iOS上,我们使用了一系列的自建框架,以及系统原生组件。在网络层,我们用NSURLSession发起请求,用Mantle解析JSON并映射到模型。我们还有一层基于NSKeyedArchiver的缓冲层。对于将条目渲染为共同主题的列表,我们有一个通用方法,这让我们能够快速为不同类型的内容构建新列表。文章界面是一个定制布局的UICollectionView。我们使用共享组件来渲染全文界面和预览界面。

  应用代码的每一次提交都会编译后推送给Medium员工,这样我们能够很快尝试新版本。应用商店的版本是滞后于新版本的,但我们也一直在尝试更快的发布,虽然可能仅仅是几处小更新。

  对于测试,我们使用XCTest和OCMock。

  Android

  在Android方面,我们与当前的SDK和支持库版本保持一致。我们并没有使用任何复杂的框架,而是倾向于为重复出现的问题构建持续性的模式。我们利用guava弥补Java中所有的缺失。另一方面来讲,我们也倾向于使用第三方库来解决特别的问题。我们还利用protocol buffers定义了API,用以生成应用中的对象。

  我们利用mockitorobolectric。我们会开发一些高层测试来运转activity和poke:刚添加screen或要重构的时候,先创建一些基本的版本,随着我们复现bug它们也会进化。我们还会开发一些底层测试,来检测一个特定的类:随着新功能的增加我们会创建测试,这能够帮助我们思考和设计底层是如何交互的。

  每个提交都会作为alpha版本自动推送到play商店,然后到Medium员工(包括我们的Hatch,Medium内部版)。推送大部分发生在周五,我们会把alpha版本发送给测试小组,请他们用整个周末进行测试。然后,周一我们会从beta版推进至正式产品版。因为最近一批代码总是随时可以推送,因此一旦发现很严重的bug,我们就可以立即修复正式产品版。当我们怀疑某些新功能的时候,可以给测试小组更长的时间。开发比较亢奋的时候,也可能发布地更加频繁。

  AB测试及其他

  我们所有的客户端都用了服务器端提供的功能标记,称为variants,用于AB测试以及指导未完成功能的开发。

  剩下还有一些框架相关的内容我没有提及:Algolia让我们在搜索相关功能上快速迭代,SendGrid处理邮件,Urban Airship用来发送提醒,SQS用来处理队列,Bloomd用作布隆过滤器,PubSubHubbubSuperfeedr用作提供RSS等等。

  编译、测试和部署

  我们积极拥抱持续集成技术,随时随地准备发布,使用Jenkins来负责相关事宜。

  我们曾经使用Make作为编译系统,但是后来迁移到Pants

  测试方面我们采用单元测试和HTTP层面功能测试两者结合的方式。所有提交的代码都需要通过测试才能够合并。我们跟Box团队合作,利用Cluster Runner来分布式运行测试,保证效率,而且能够和GitHub很好的整合在一起。

  我们大概不到15分钟就可以把某阶段的系统部署,顺利编译通过,留作正式产品的备选。主应用服务器通常一天要部署五次,多的时候十次。

  我们采用蓝绿部署。正式产品版本的流量发送给一个canary实例,发布进程会监控部署过程的错误率,必要时候通过调整内部DNS回滚。

  面向未来

  到此,讲了足够多的干货!为了重构产品,获得更好的阅读体验,还有很长的路要走。我们仍然在努力为作者和发布者设计更多的功能。打比方来讲,线上阅读还是一片绿地,面对它有着无限可能,我们始终抱着开放的心态设计和实现功能。未来我们会努力用各种功能为用户提供高质量内容和价值。

Medium开发团队谈架构设计_转的更多相关文章

  1. 老徐FrankXuLei 受邀为花旗银行讲授《微软WCF服务分布式开发与SOA架构设计课程》

    老徐FrankXuLei 受邀为花旗银行上海研发中心讲授<微软WCF服务分布式开发与SOA架构设计课程> 受邀为花旗银行上海研发中心讲授<微软WCF服务分布式开发与SOA架构设计课程 ...

  2. Android开发-浅谈架构(二)

    写在前面的话 我记得有一期罗胖的<罗辑思维>中他提到 我们在这个碎片化 充满焦虑的时代该怎么学习--用30%的时间 了解70%该领域的知识然后迅速转移芳草鲜美的地方 像游牧民族那样.原话应 ...

  3. 使用 Unity 3D 开发游戏的架构设计难点

    Unity 3D 引擎对于开发者来说,入手非常快,因为它采用的是 C# 作为开发语言,这也大大降低了开发者的门槛.但凡只要懂一门编程语言的人都能使用 Unity 3D 引擎开发,另外 Unity 3D ...

  4. Android开发-浅谈架构(一)

    写在前面的话 嗯 聊聊架构. 这段时间一直在维护旧项目. 包括自己之前写的新项目 越来越发现 一个架构清晰的项目往往让人赏心悦目.不至于在一个bug丢过来之后手足无措.包括以后别人接收自己的项目 能很 ...

  5. 谈架构设计中DDD思想的运用

    首先,描述一下我的业务场景及项目分层结构,非标准DDD(其实我不觉得有标准),只是思考的时候有带入DDD思想. 业务场景:这是一个ERP系统对中台提供的接口项目,仓储操作大多都是存储过程去完成的. 项 ...

  6. iOS开发之浅谈MVVM的架构设计与团队协作

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  7. 浅谈iOS中MVVM的架构设计与团队协作

    说到架构设计和团队协作,这个对App的开发还是比较重要的.即使作为一个专业的搬砖者,前提是你这砖搬完放在哪?不只是Code有框架,其他的东西都是有框架的,比如桥梁等等神马的~在这儿就不往外扯了.一个好 ...

  8. IOS中 浅谈iOS中MVVM的架构设计与团队协作

    今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  9. 浅谈iOS中MVVM的架构设计与团队协作【转载】

    今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

随机推荐

  1. ekho安装及测试(中文文字转语音)

    1. 官网下载源码包 地址:http://www.eguidedog.net/ekho.php 2. 安装 xz -d ekho-7.5.tar.xz tar -xvf ekho-7.5.tar ap ...

  2. 一点做用户画像的人生经验:ID强打通

    1. 背景 在构建精准用户画像时,面临着这样一个问题:日志采集不能成功地收集用户的所有ID,且每条业务线有各自定义的UID用来标识用户,从而造成了用户ID的零碎化.因此,为了做用户标签的整合,用户ID ...

  3. webservice复杂类型实例

    1.准备工作: 概念:SOAP(简单对象访问协议).WSDL(web服务描述语言).XML(可扩展标记语言).axis(阿帕奇可扩展交互系统) (1)     下载axis1.4,将axis1.4中的 ...

  4. 将逗号分隔的字符串与List互转

    将逗号分隔的字符串与List互转 方法 1: 利用JDK的Arrays类String str = "a,b,c";List<String> result = Array ...

  5. SG仿真常用模块

    workspace交互 配合gateway in/out,实现信号仿真与workspace的互联. 滤波器 可与FDATool同时使用,直接关联FDATool的参数,而不必输入FDATool的滤波器系 ...

  6. Four Node.js Gotchas that Operations Teams Should Know about

    There is no doubt that Node.js is one of the fastest growing platforms today. It can be found at sta ...

  7. unity, Collider2D.attachedRigidbody

    boss根节点上挂RigidBody2D(且boss根节点以下任何子节点均不挂RigidBody2D),boss腿部骨骼节点挂collider2D,标签为"bossLeg",bos ...

  8. Maven报错 解决方案。ERROR: No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id

    报错: [ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or ...

  9. 在Windows系统上怎么使用SecureCRT链接Linux AWS EC2 -摘自网络

    在Windows系统上就需要使用SecureCRT,Putty等工具,进行连接.但是AWS提供的XXX.pem文件,需要做一些处理SecureCRT的方法: 1.使用XXX.pem文件生成一个公钥文件 ...

  10. 腾讯云服务器 安装fastdfs文件服务器

    上篇安装完nginx后,那么这次咱们就来安装fastdfs文件服务器,为何要使用文件服务器,这里不多说了,以前的文章有写过 首先用ftp工具把fastdfs的相关文件上传至腾讯云,如下 首先,安装基本 ...