原文: A Brief History of Scaling LinkedIn

2003年是LinkedIn元年,公司成立的目标是连接你的个人人脉以获得更好的的工作机会。上线第一周才有2700个会员注册,时光飞梭,LinkedIn的产品、会员数量、服务器负载都极大的增长了。
今天,LinkedIn全球用户已经超过3.5亿。我们每秒有数十万个页面被访问,移动端流量已占到50%以上 (mobile moment)。所有这些请求都从后台获取数据,而我们的后台系统可以处理每秒上百万次查询。

问题来了: 所有这些是怎么做到的呢?

早期

1. Leo

和现在很多站点开始的时候一样, LinkedIn使用一个应用程序做所有的工作。 这个应用程序被称之为 "Leo"。它包含所有的Java Servlet页面, 处理业务逻辑, 连接少量的LinkedIn数据库。

*哈!早年网站的样式-简单实用*

2. Member Graph (会员关系图)

开始的工作之一就是管理会员之间关系的社交网络。我们需要一个系统通过图遍历(graph traversals)的方式来查询关系数据, 同时需要将数据驻留内存以便获得高效和性能。从这个不同的使用特征来看, 很明显这需要一个独立于Leo的系统以方便扩大规模,于是一个叫做"Clould"专门用于会员关系图(member graph)的独立系统诞生了。这是LinkedIn的第一个服务系统。为了和Leo系统分离,我们使用Java RPC来进行通讯。

也大约在此期间我们需要增加搜索服务的能力。我们的会员关系图服务也提供数据给一个基于Lucene的搜索服务。

3. Replica read DBs (多个只读数据库副本)

随着站点的增长, Leo系统也在扩大, 增加了更多的角色和职能, 也更加复杂。 通过负载均衡可以运行多个Leo实例,但是新增的负载也影响到LinkedIn的最关键系统-会员信息数据库。

一个最容易的解决方案就是垂直扩展 - 在其上增加更多的CPU和内存。这虽然可以支撑一段时间,但是将来我们还是会遇到规模扩展的问题。会员信息数据库既处理读又处理写。 为了扩展,我们引入了复制从库(replica slave DB)。 复制数据库是会员数据库的一个拷贝, 使用 databus (现已开源)的最早版本来进行同步。这些复制从库处理所有的读请求, 并且增加了保证主库和从库数据一致性的逻辑。

*主从读写分离的方案之后,我们转向了数据库分区的解决方案*

当站点遇到越来越多的流量时,单一的Leo系统经常宕机,而且很难排查和恢复, 发布新代码也很困难。 高可用性对LinkedIn至关重要, 很明显我们需要"干掉" Leo, 把它分解成多个小的功能模块和无状态的服务。

*"Kill Leo"这个咒语在内部传颂了好多年*

4. Service Oriented Architecture (面向服务的架构)

工程师开始抽取出一些微服务, 这些微服务提供API和一些业务逻辑, 如搜索,会员信息, 通讯和群组平台。接着我们的表现层也被抽取出来了,比如招聘产品和公共信息页。新产品,新服务都独立于Leo。 不久,各个功能区的垂直栈完成了。
我们构建了前端服务器, 可以从不同的域获取数据,处理展示逻辑以及生成HTML (通过JSP)。我们还构建了中间层服务提供API接口访问数据模型以及提供数据库一致性访问后端数据服务。到2010年,我们已经有超过150个独立的服务,而今天,我们已经有超过750个服务。

因为无状态, 规模扩展可以通过堆叠任意服务的新实例以及在它们之间进行负载均衡来完成。我们给每个服务设定了警戒红线, 知道它的负载能力, 提供早期预警和性能监控。

5. cache (缓存)

LinkedIn可预见的增长促使我们要进一步的扩展。我们知道通过添加更多的缓存层以减少负载压力。很多应用开始引入中间缓存层如 memecached 或者 couchbase。 我们还在数据层增加了缓存, 并且在适当的时候使用 Voldemort 提供预先计算的结果。

之后,我们实际上去掉了中间缓存层。中间缓存层存储来自多个域的数据。虽然开始时缓存看起来是减少压力的一种简单方式,但是缓存数据失效的复杂性和调用图(call graph)变得无法控制。将缓存更可能地接近数据层可以降低延迟, 使我们可以水平扩展,降低可知的负载(cognitive load)。

6. Kafka

为了收集日益增长的数据,LinkedIn开发了很多定制的数据通道来流水化和队列化数据(streaming and queueing)。 比如, 我们需要将数据放入数据仓库,我们需要将一批数据放入Hadoop工作流以便分析,我们从每个服务中中聚合了大量日志, 我们收集了很多用户追踪事件如页面点击, 我们需要队列化inMail消息系统中的数据, 我们需要保证用户更新完个人信息后搜索数据也是最新的等等。
随着网站还在壮大,更多的定制管道出现了。 因为网站规模需要扩展,每一个独立的管道也需要扩展, 有些东西不得不放弃。 结果就是Kafka开发出来了, 它是我们的分布式的发布订阅消息系统。Kafka成为一个统一的管道, 根据commit log的概念构建, 特别注重速度和扩展性。 它可以接近实时的访问数据源,驱动Hadoop任务, 允许我们构建实时的分析,广泛地提升了我们的站点监控和报警能力, 也使我们能够可视化和跟踪调用图(call graph)。 今天, Kafka
每天处理超过5千亿的事件

7. Inversion(反转)

扩展可以从很多维度来衡量,包括组织结构。 在2011年底, LinkedIn开始了一个内部创新,叫 “反转” (Inversion)。我们暂停了新功能的开发, 允许整个工程部门专注于提升工具,部署,基础架构和开发者生产力上。它成功地使我们可以敏捷地建立可扩展性新产品。

近几年

1. Rest.li

当我们从Leao转向面向服务的架构后,之前抽取的基于Java RPC的API, 在团队中开始变得不一致了,和表现层耦合太紧,这只会变得更糟。为了解决这个问题, 我们开发了一个新的API模型,叫做 Rest.li. Rest.li 符合我们面向数据模型的架构, 确保在整个公司提供一致性的无状态的Restful API模型。
基于HTTP的JSON数据, 我们新的API最终很容易地编写非Java的客户端。 LinkedIn今天仍然主要使用Java栈,但是也有很多使用Python, Ruby, Node.js 和 C++的客户端,可能是自己开发的或者收购过来的。 脱离了RPC也让我们将变现层和后端兼容型的问题中挣脱出来。另外, 使用Dynamic Discovery (D2)的Rest.li, 我们可以得到自动的基于负载均衡,服务发现和可扩展的API客户端。
今天, LinkedIn有975 个Rest.li资源, 所有的数据中心每天有超过一千亿级Rest.li调用。

Rest.li R2/D2 技术站

2. Super Blocks (超级块)

面向服务的架构很好的解耦了域之间的联系和可以独立地扩展服务。但是也有缺点, 很多应用获取各种类型的不同的数据, f(call graph)或者叫做"扇出" (fanout)。例如, 任意一次个人信息页的请求就会获取照片,会员关系, 组,订阅信息, 关注,博客,人脉,推荐等信息。 这个调用图很难管理,而且越来越难控制。
我们引入了超级块的概念。 为一组后台服务提供一个单一的访问API。这样我们就可以有一个team专门优化这个块,同时保证每个客户端的调用图可控。

3. Multi-Data Center (多数据中心)

作为一个会员快速增长的全球化公司,我们需要从一个数据中心进行扩展,我们通过几年的努力来解决这个问题,首先,从两个数据中心(洛杉矶 和 芝加哥)提供了公共个人信息,证明可行后,我们开始增强服务来处理数据复制、不同源的调用、单向数据复制事件、将用户分配到地理位置更近的数据中心。
我们大多的数据库运行在Espresso(一个新的内部多用户数据仓库)上。
Espresso支持多个数据中心,提供了 主-主 的支持,及支持很难的数据复制。

多个数据中心对于高可用性具有不可思议的重要性,你要避免的单点故障不仅仅是某个服务失效,更要担心整个站点失效。今天,LinkedIn运行了3个主数据中心,同时还有全球化的PoPs服务。

LinkedIn's operational setup as of 2015 (circles represent data centers, diamonds represent PoPs)

4. 我们还做了哪些工作?

当然,我们的扩展故事永远不会这么简单。我们的工程和运维团队这些年做了不计其数的工作,主要包括这些大的创新:
这些年很多最关键系统都有自己丰富的扩展演化历史,包括会员图服务(Leo之外的第一个服务),搜索(第二个服务),新闻种子,通讯平台及会员信息后台。

我们还构建了数据基础平台支持长期的增长,这是Databus和Kafka的第一次实战,后来用Samza做数据流服务,Espresso和Voldemort作存储解决方案,Pinot用来分析系统,以及其它自定义解决方案。另外,我们的工具也得到了提升,这样工程师就可以自动化布署这些基础架构。

我们还使用Hadoop和Voldemort数据开发了大量的离线工作流,用以智能分析,如“你可能认识的人”,“相似经历”,“感觉兴趣的校友”及“个人简历浏览地图”。

我们重新考虑了前端的实现,增加客户端模板到混合页面(个人中心、我的大学页面),这样应用可以更加可交互,只要我们的服务器发送JSON或部分JSON数据。此外,模板页面通过CDN和浏览器缓存。我们也开始使用了BigPipe和Play框架,把我们的模型从线程化的服务器变成非阻塞异步的服务器。

除了代码,我们使用了Apache Traffic Server做多层代理和用HAProxy做负载均衡,数据中心,安全,智能路由,服务端渲染等等。

最后,我们继续提升服务器的性能,包含优化硬件,内存和系统的高级优化,使用更新的JRE。

5. 下一步

LinkedIn今天仍在快速增长,仍有大量值得提升的工作要做,我们正在解决一些问题,看起来只解决了一部分 - 快来加入我们吧!

感谢Steve, Swee, Venkat, Eran, Ram, Brandon, Mammad, 和 Nick的审阅和帮助

LinkedIn架构这十年的更多相关文章

  1. [转载] LinkedIn架构这十年

    原文: http://colobu.com/2015/07/24/brief-history-scaling-linkedin/ 原文: A Brief History of Scaling Link ...

  2. Java架构师-十项全能学习笔记(1)

    Java架构师-十项全能学习笔记(1) @Configuration @EnableStateMachine public class OrderStateMachineConfig extends ...

  3. 转: GUI应用程序架构的十年变迁:MVC,MVP,MVVM,Unidirectional,Clean

    十年前,Martin Fowler撰写了 GUI Architectures 一文,至今被奉为经典.本文所谈的所谓架构二字,核心即是对于对于富客户端的 代码组织/职责划分 .纵览这十年内的架构模式变迁 ...

  4. 《InsideUE4》GamePlay架构(十)总结

    世界那么大,我想去看看 引言 通过对前九篇的介绍,至此我们已经了解了UE里的游戏世界组织方式和游戏业务逻辑的控制.行百里者半九十,前述的篇章里我们的目光往往专注在于特定一个类或者对象,一方面固然可以让 ...

  5. GUI应用程序架构的十年变迁:MVC,MVP,MVVM,Unidirectional,Clean

    十年前,Martin Fowler撰写了 GUI Architectures 一文,至今被奉为经典.本文所谈的所谓架构二字,核心即是对于对于富客户端的 代码组织/职责划分 .纵览这十年内的架构模式变迁 ...

  6. GPS部标平台的架构设计(十)-基于Asp.NET MVC构建GPS部标平台

    在当前很多的GPS平台当中,有很多是基于asp.NET+siverlight开发的遗留项目,代码混乱而又难以维护,各种耦合和关联,要命的是界面也没见到比Javascript做的控件有多好看,随着需求的 ...

  7. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力

    ​ 飞桨 (PaddlePaddle) 以百度多年的深度学习技术研究和业务应用为基础,是中国首个自主研发.功能完备. 开源开放的产业级深度学习平台,集深度学习核心训练和推理框架.基础模型库.端到端开发 ...

  8. Linux进阶文档丨阿里架构师十年Linux心得,全在这份文档里面

    Linux是什么 Linux就是个操作系统: 它和Windows XP.Windows 7.Windows 10什么的一样就是一个操作系统而已! Linux能干什么: 它能当服务器,服务器上安装者各种 ...

  9. Java系的大网站架构-LinkedIn和淘宝

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

随机推荐

  1. 洛谷P4331 [BOI2004] Sequence 数字序列 [左偏树]

    题目传送门 数字序列 题目描述 给定一个整数序列 a1​,a2​,⋅⋅⋅,an​ ,求出一个递增序列 b1​<b2​<⋅⋅⋅<bn​ ,使得序列 ai​ 和 bi​ 的各项之差的绝对 ...

  2. 洛谷P2224 [HNOI2001] 产品加工 [DP补完计划,背包]

    题目传送门 产品加工 题目描述 某加工厂有A.B两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成.由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时 ...

  3. Codeforces Round #113 (Div. 2) Tetrahedron(滚动DP)

    Tetrahedron time limit per test 2 seconds memory limit per test 256 megabytes input standard input o ...

  4. 面向对象编程课程(OOP)第二单元总结

    一.设计策略 第一次作业(傻瓜式电梯): 由于是第一次写多线程作业,许多的知识还处在理论阶段,所以第一次作业写得非常的朴实无华.整个程序总共有四个类,Main类负责通过电梯类实例化一个电梯,然后通过w ...

  5. 使用Nginx的配置对cc攻击进行简单防御

    ddos攻击:分布式拒绝服务攻击,就是利用大量肉鸡或伪造IP,发起大量的服务器请求,最后导致服务器瘫痪的攻击. cc攻击:类似于ddos攻击,不过它的特点是主要是发起大量页面请求,所以流量不大,但是却 ...

  6. mysql无法输入中文排错

    题记:以前都是使用可视化界面创建数据库,进行操作的,但是今天使用cmd窗口进行操作发现出错了. 以前记得自己使用cmd也是可以正确操作的,但是这次却出错了,在网上找了很多解决的办法,最后还是靠自己慢慢 ...

  7. 【stl小记】list

    list相当于双向链表,所以快插快删比较方便(链式数据结构的性质),但是随机读取较慢 用一道luogu的水题做一做list,code如下 #include <cstdio> #includ ...

  8. 【动态规划】Round Subset

    CF837D. Round Subset Let's call the roundness of the number the number of zeros to which it ends. Yo ...

  9. BZOJ 1827 [Usaco2010 Mar]gather 奶牛大集会(树形DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1827 [题目大意] 给出一棵有点权和边权的树, 请确定一个点,使得每个点到这个点的距离 ...

  10. 【循环节】【矩阵乘法】MIPT-2016 Pre-Finals Workshop, Taiwan NTU Contest, Sunday, March 27, 2016 Problem F. Fibonacci of Fibonacci

    题意:F(n)为斐波那契数列的第n项,问你F(F(n)) mod 20160519的值. 发现有循环节,F(26880696)=0,F(26880697)=1,.... 于是两次矩乘快速幂即可. #i ...