Instagram的技术架构

http://blogread.cn/it/article/5497html

 Instagram 被 Facebook 以10亿美金收购。团队规模:13 人。而在被Facebook收购前的一个月,整个团队才7名员工。python

  • 2010年: 2位工程师
  • 2011年: 3 位工程师
  • 2012年: 5 位工程师

    制胜法宝:nginx

  • 普遍的单元测试和功能测试
  • 坚持DRY(Don’t Repeat Yourself)原则
  • 使用通知/信号机制实现解耦
  • 咱们大部分工做使用Python来完成,只有逼不得已的时候,才会用C
  • 频繁的代码复查,尽可能保持“智慧共享”。(frequent code reviews, pull requests to keep things in the ‘shared brain’)
  • 普遍的系统监控

    Instagram的两个创始人git

  • Mike Kriegerr:以前是一个颇为低调的工程师和用户体验设计师,他在一家名叫Meebo的创业公司工做了1年半。analytics & python @ meebo(在Meebo作分析,使用python );
  • Kevin Systrom:毕业后在Google的收购部门工做了一年,今年28岁,随后去到了一家从事旅行业务的创业公司Nextstop,没有计算机学位,没有接受过正式培训, 但他下班后坚持自学编程,在这家创业公司被Facebook以人才收购的方式收购后,Systrom又去早期的Twitter实习了一段时间。

    下面一块儿来看下这个奇迹是怎样搭建的?Instagram的技术实现是什么?如下内容来自翻译。github

    当咱们与其余工程师偶遇和交流的时候,有一个问题常常被问及,“大家的技术架构(technology stack)是怎么样的”?咱们以为从较高的层次来描述Instagram的全部构成系统是一件有趣的事情;将来你可能期待更深刻的描述这些系统。这就是咱们的系统,仅仅1年时间,而且咱们活了下来,其中有一部分咱们一直在修改。一个小型团队的初创公司,能够在一年多一点时间发展到1400多万用户规模。redis

    Instagram 开发团队奉行的三个核心原则:sql

  • Keep it very simple (极简主义)
  • Don’t re-invent the wheel (不重复发明轮子)
  • Go with proven and solid technologies when you can(能用就用靠谱的技术)

    操做系统/主机数据库

    咱们在Amazon EC2上跑Ubuntu Linux 11.04 (“Natty Narwhal”),这个版本通过验证在 EC2 上够稳定。咱们发现以前的版本在EC2上高流量的时候都会出现各类不可预测的问题( freezing episodes)。由于只有3名工程师,咱们的需求依然在不断的变化中,所以自托管主机不是咱们的选择,也许将来当用户量空前增加的时候,咱们会考虑。apache

    负载均衡django

     每个对Instagram 服务器的访问都会经过负载均衡服务器;咱们使用2台nginx机器作DNS轮询。这种方案的缺点是当其中一台退役时,须要花时间更新DNS。最近,咱们转而使用Amazon的ELB(Elastic Load Balancer)负载均衡器,使用3个NGINX 实例能够实现调入调出(and are automatically taken out of rotation if they fail a health check);咱们同时在 ELB 层停掉了 SSL , 以缓解nginx的 CPU 压力。咱们使用Amazon的Route53服务做为DNS服务,他们最近在AWS控制台上增长了一套很好的GUI工具。

    应用服务器

    接下来是应用服务器用来处理咱们的请求。咱们在Amazon的High-CPU Extra-Large机器上运行了Django ,随着用户的增加,咱们已经在上面跑了25个Django实例了(幸运地,由于是无状态的,因此很是便于水平扩展)。咱们发现咱们的个别工做负载是属于计算密集型而非IO密集型,所以High-CPU Extra-Large类型的实例恰好提供了合适的比重(CPU和内存)。

    使用 Gunicorn 做为 WSGI 服务器。过去曾用过 Apache 下的 mod_wsgi 模块,不过发现 Gunicorn 更容易配置而且节省 CPU 资源。使用 Fabric 加速部署。Fabric最近增长了并行模式,所以部署只须要花费几秒钟。

    数据存储

    咱们大部分数据(用户信息,照片的元数据、标签等)存储在PostgreSQL中;咱们 以前已经说了关于如何基于不一样的Postgres 实例进行切分的。咱们的主要分片集群包含12个四倍超大内存云主机(且12个副本在不一样的区域);

    咱们发现亚马逊的网络磁盘系统(EBS)每秒的寻道能力不够,所以,将咱们全部工做放到内存中就变得尤其重要。为了得到合理的性能,建立了软 RAID 以提高 IO 能力,使用的 Mdadm 工具进行 RAID 管理;

    顺便提一下,咱们发现vmtouch用来管理内存数据是个极好的工具,尤为是在故障转移时,从一台机器到另外一台机器,甚至没有活动的内存概要文件的状况。这里是脚本,用来解析运行于一台机器上的vmtouch 输出并打印出相应vmtouch命令,在另外一台机器上执行,用于匹配他当前的内存状态;

    咱们全部的PostgreSQL实例都是运行于主-备模式(Master-Replica),基于流复制,而且咱们使用EBS快照常常备份咱们的系统。为了保证咱们的快照的一致性(原始灵感来源于ec2-consistent-snapshot)咱们使用XFS做为咱们的文件系统,经过XFS,当进行快照时,咱们能够冻结&解冻RAID阵列。为了进行流复制,咱们最爱的工具是repmgr 。

    对于从咱们的应用服务器链接到数据,咱们很早就使用了Pgbouncer作链接池,此举对性能有巨大的影响。咱们发现Christophe Pettus的博客 有大量的关于Django、PostgreSQL 和Pgbouncer 秘诀的资源。

    照片直接存储在亚马逊的S3,当前已经存储了几T的照片数据。咱们使用亚马逊的CloudFront做为咱们的CDN,这加快了全世界用户的照片加载时间(像日本,咱们第二最受欢迎的国家)

    咱们也普遍的使用了Redis ; 咱们的main feed、activity feed、sessions系统(这里是咱们的Django session backend),和其余 相关系统 都使用了Redis。由于全部的Redis数据都须要放在内存中,所以咱们最后使用了几个四倍超大内存云主机用于跑Redis。咱们的Redis也是运行于主-备模式(Master-Replica),而且常常将DB保存到磁盘,最后使用EBS快照备份这些数据(咱们发如今主机上进行导出太费劲了)。因为Redis 容许写入备份,使得在线故障转移很是方便,转移到一台新的Redis 机器,而不须要停机。

    为了咱们的geo-search API,咱们一直使用PostgreSQL了不少个月,不事后来迁移到了Apache Solr.他有一套简单的JSON接口,这样咱们的应用程序相关的,只是另外一套API而已。

    最后,和任何现代Web服务同样,咱们使用了Memcached 作缓存,而且当前已经使用了6个Memcached 实例,咱们使用pylibmc & libmemcached进行链接。Amzon最近启用了一个灵活的缓存服务(Elastic Cache service),可是它并不比运行咱们本身的实例便宜,所以咱们并无切换上去;

    任务队列&推送通知

    当一个用户决定分享一张Instagram 的照片到Twitter 或Facebook,或者是当咱们须要通知一个 实时订阅者有一张新的照片贴出,咱们将这个任务推到 Gearman,一个任务队列系统可以写于Danga。这样作的任务队列异步经过意味着媒体上传能够尽快完成,而“重担”能够在后台运行。咱们大概有200个工做实例(都用Python写的)在给定的时间内对队列中的任务进行消费,并分发给不一样的服务。咱们的feed feed fan-out也使用了Gearman,这样posting就会响应新用户,由于他有不少followers。

    对于消息推送,咱们找到的最划算的方案是https://github.com/samuraisam/pyapns,一个开源的Twisted 服务,已经为咱们处理了超过10亿条通知,而且绝对可靠。

    监控

    对于100多个实例,监控变得很是重要。咱们使用Munin进行图形化度量。咱们基于 Python-Munin,也写了不少Munin 插件。用于图形化度量非系统级的东西(例如,每秒的签入人数,每条照片发布数等)咱们使用Pingdom做为外部监控服务,PagerDuty 用于事件通知。

    Python错误报告,咱们使用Sentry,一个Disqus的工程师写的使人敬畏的开源的Django app。在任什么时候间,咱们能够开始工做,并实时的查看咱们的系统发生了什么错误。

    以上内容翻译自:http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances-dozens-of

    Instagram的技术变迁

    创业初期:

  • 初存储采用CouchDB(Apache CouchDB 是一个面向文档的数据库管理系统)
  • 最初只有一台服务器(在洛杉矶),比一台比MacBook Pro强不到哪里去。

    技术更新

    

    1、数据库扩展

    早期使用django ORM+postgresql,由于PostGIS,选择了postgresql。(PostGIS在对象关系型数据库PostgreSQL上增长了存储管理空间数据的能力,至关于Oracle的spatial部分)而且数据库部署在独立服务器上。

    由于EC2机器的最大内存为68G,随着照片存储量的增长,进行垂直分区(vertical partitioning);使用django db routers,作垂直分区变得很容易;以下:照片则映射到photodb

def db_for_read(self, model):
    if app_label == 'photos':
        return 'photodb'

    当照片存储量大于60G的时候,采用水平分区(也就是所谓的“分片”sharding)

    sharding带来的问题:

    一、数据的检索,hard to know what your primary access patterns will be w/out any usage in most cases, user ID

    二、当有分片变得太大的时候怎么办?

    基于范围的分片策略(就像MongoDB那样)

    

    三、性能有降低趋势,尤为在EC2上,缘由:disk IO,解决方法:预先切分(pre-split),即预先切分上千个逻辑切片,将它们映射到较少的物理分区节点中去。

    关于相关内容,更详细的能够参看这里

    2、选择合适工具

    进行缓存/反规范化数据设计

    用户上传图片时:

  • 用户上传带有标题信息和地理位置信息(可选)的照片;
  • 同步写到这个用户对应的数据库(分片)中;
  • 进行队列化处理
  • a、若是带有地理位置信息,经过异步的POST请求,将这个图片的信息送到Solr(Instagram 用于geo-search API的全文检索服务器)。
  • b、跟随者的信息分发(follower delivery),即告诉个人follower ,我发布了新的照片。如何来实现的呢?每一个用户都有一个follower 列表,新照片上传时会把照片ID发送给列表中的每个用户,用Redis 来处理这一业务简直太棒了,快速插入,快速子集化(rapid subsets,什么意思?是指获取部分数据吗?
  • c、when time to render a feed,we take small # of IDs, go look up info in memcached(当须要生成feed的时候,咱们经过ID+#的格式,直接在memcached中查找信息)

    Redis适合什么样的场景?

  • 数据结构相对有限;
  • 对频繁GET的地方,对复杂对象进行缓存;

    不要将本身绑定在非得之内存数据库为主要存储策略的方案上(don’t tie yourself to a solution where your in-memory DB is your main data store

    参考连接:http://www.cnblogs.com/xiekeli/archive/2012/05/28/2520770.html

相关文章
相关标签/搜索