4

Instagram的技术架构

 3 years ago
source link: https://blogread.cn/it/article/5497?f=hot1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Instagram的技术架构

浏览:7783次  出处信息

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

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

    制胜法宝:

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

    Instagram的两个创始人

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

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

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

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

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名工程师,我们的需求依然在不断的变化中,因此自托管主机不是我们的选择,也许未来当用户量空前增长的时候,我们会考虑。

    负载均衡

     每一个对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强不到哪里去。

    技术更新

    一、数据库扩展

    早期使用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带来的问题:

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

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

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

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

    关于相关内容,更详细的可以参看这里

    二、选择合适工具

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

    用户上传图片时:

用户上传带有标题信息和地理位置信息(可选)的照片; 同步写到这个用户对应的数据库(分片)中; 进行队列化处理 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

觉得文章有用?立即:

和朋友一起 共学习 共进步!

建议继续学习:

  1. 作为新星,就应该是这个样子!    (阅读:1340)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK