Medium开发团队谈架构设计_转

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

 背景android

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

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

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

  团队介绍golang

  提及团队我很是自豪,这是一群富有好奇心并且想法丰富的天才,你们凑到一块是想作大事的。web

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

  在工做组织方面,咱们有着很大的自由度,固然做为一个公司组成,咱们仍是有季度目标的,而且鼓励敏捷开发模式。咱们使用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回滚。

  面向将来

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

相关文章
相关标签/搜索