LinkedIn成立于2003年,主要目标是连接你的个人网络以获得更好的工作机会。在推出的第一周,只有2,700名会员。在接下来的几年里,LinkedIn的产品、会员和服务器负载都增长得非常快。如今,LinkedIn在全球拥有超过3.5亿用户。我们每天每秒访问的页面数以万计,移动端流量已经占到了50%以上。所有这些接口请求都是从后台获取的,每秒可达100%。那么,我们该怎么做呢?早年-LeoLinedIn像许多网站一样开始,只有一个应用程序服务完成所有工作。我们将此应用程序命名为“Leo”。它包含所有网站页面(JAVAServlets),还包含业务逻辑,并连接到一个轻量级的LinkedIn数据库。哈!早年网站的形式——简单实用的会员图表首先要做的是管理会员之间的社交网络。我们需要一个系统来以图形方式遍历用户之间连接的数据,同时保持内存驻留以提高效率和性能。看看这种多样化的需求,很明显我们需要可独立扩展的Leo。一个独立的会员图标化系统,一个叫做“云”的服务诞生了——LinkedIn的第一个服务。为了保持图表服务独立于Leo,我们使用JavaRPC进行通信。大概在这个时期,我们也有搜索服务的需求,会员图表服务也在向搜索引擎-Lucene提供数据。复制只读数据库随着网站的不断发展,Leo也在不断发展,增加了角色和职责,当然也增加了复杂性。负载均衡的多实例Leo已启动并正在运行。但增加的负载也影响了其他关键的LinkedIn系统,例如其会员信息数据库。一个常见的解决方案是进行垂直扩展——即添加更多的CPU和内存!虽然以时间换取,但我们以后会扩大。会员信息数据库接受读写请求。为了扩展,出现了一个复制的从数据库。这个复制的从数据库是成员主数据库的副本。早期的databus用于保证数据同步(现已开源)。从库接管所有读请求,并添加逻辑保证从库与主库一致。当主从架构工作了一段时间后,我们转向数据库分区。当网站开始看到流量越来越大时,我们的应用服务Leo在生产环境中经常宕机。很难排除故障和恢复,也很难发布新代码。困难的是,高可用性对LinkedIn来说至关重要,很明显我们会“杀死”Leo并将其拆分为小的功能块和无状态服务。面向服务的架构——SOA工程师首先分解负担API接口和业务逻辑的微服务,如搜索、个人信息、消息和群组平台,然后分解表现层,如招聘功能和公共介绍页面。新产品和服务被放置在Leo之外,很快每个功能区域的垂直服务堆栈就完成了。我们构建了前端服务器,从不同的领域抓取数据模型进行展示,例如生成HTML(通过JSP)。我们还构建了提供API接口访问数据模型的中间层服务和提供对数据库的一致访问的后端数据服务。到2010年,我们拥有超过150项个人服务,而今天,我们拥有超过750项服务。因为是无状态的,所以可以直接堆叠新的服务实例,使用硬件负载均衡来实现扩展。我们对每项服务进行了红线标注,以便我们了解其负载,以便进行早期对策和性能监控。缓冲LinkedIn的可预测增长,因此需要进一步扩张。我们知道,通过增加更多的缓存,我们可以减少访问的密集压力。很多应用开始使用memcached/couchbase等中间层缓存,数据层也加入了缓存。在某些场景中,使用Voldemort来提供预结果生成。一段时间后,我们实际上摆脱了很多中间层缓存,中间层缓存数据往往来自多个域,但缓存最初只是用于减少负载,但更新缓存和生成图的复杂性变得更多难以控制。保持缓存最接近数据层将减少不可控影响的可能性,同时还允许水平扩展,减少分析负载。为了从Kafka收集越来越多的数据,LinkedIn开启了很多自定义的数据管道对数据进行流式和队列化,比如我们需要将数据保存到数据仓库,我们需要将批量数据发送到Hadoop工作流进行分析,我们从每个服务聚合了大量的日志,我们跟踪很多用户行为,比如页面点击,我们需要实例化InMail消息服务,我们需要保证用户更新个人信息时搜索到的数据是最准确的,等等。当站点仍在增长时,会出现更多定制的管道服务。因为网站需要可扩展,所以单独的管道也必须是可扩展的。有时很难选择。解决方案是使用我们的分布式发布-订阅消息传递平台kafka。Kafka变成了一个统一的管道服务,它从提交的日志中生成摘要,同时从一开始就快速且可扩展。它可以近乎实时地访问所有数据源,驱动Hadoop任务,使我们能够构建实时分析,广泛改进我们的站点监控和警报功能,并支持呼叫可视化。如今,Kafka每天处理超过5亿个事件。反向扩展可以沿多个维度进行衡量,包括组织结构。2011年底,LinkedIn开始了一项名为“Inversion”的创新。我们暂停了新功能开发,让所有开发部门专注于改进工具和部署、基础设施和实用程序开发。这有助于当今新的可扩展产品的敏捷开发。近几年–Rest.li当我们从Leao转向面向服务的架构时,之前基于JAVA的RPC接口已经开始在团队中分裂,与表现层的联系过于紧密。这只会变得更糟。为了解决这个问题,我们开发了一个名为Rest.li的新API模型,它是一个以数据为中心的架构,同时保证整个公司业务的数据一致性和无状态的RestfulAPI。基于HTTPJSON数据传输,我们的新API最终可以轻松支持非java编写的客户端。LinkedIn今天仍然主要使用Java,但根据现有的技术分布,也使用Python、Ruby、Node.js和C++。与RPC分离,也让我们可以解耦前端表现层和后端兼容性问题。另外,使用基于动态发现技术(D2)的Rest.li,我们的每一个服务层API都获得了自动负载均衡、发现和扩展的能力。今天,所有LinkedIn数据中心每天有超过975个Rest.li资源和超过1000亿个Rest.li调用。Rest.liR2/D2技术栈超级块的面向服务的架构,对于领域解耦和服务独立扩展性非常有帮助,但是缺点是我们大部分的应用需要的数据类型很多,会有上百种产品顺序延长电话。这通常被称为“调用图”,或具有如此多扩展调用的“扇出”。例如,任何个人信息页面包含的不仅仅是相册、连接、组、订阅、关注者、博客、网络维度、推荐等等。调用图可能难以管理,只会使事件变得越来越不规则。我们使用“超级块”的概念——具有单一API接口的分组后台服务。这允许一个小组优化一个“块”,同时控制每个客户端的调用。多个数据中心作为一家成员快速增长的全球性公司,我们需要扩展数据中心。多年来,我们一直在努力解决这个问题。首先,公开的个人信息是从两个数据中心(洛杉矶和芝加哥)提供的,这表明,我们已经能够提供增强的数据复制服务,不同来源的远程调用,分离数据复制事件,以及将用户分配到地理上更近的数据中心。我们的大部分数据库都在Espresso(一种新的内部多用户数据仓库)上运行。Espresso支持多数据中心,提供master-master支持,支持复杂的数据复制。多个数据中心对于高可用性非常重要,您希望避免单点故障,这种故障不仅会导致服务失败,而且会担心整个站点出现故障。如今,LinkedIn运营着3个主要数据中心,以及全球PoP服务。我们还做了什么?当然,我们的延伸故事绝非如此简单。我们的工程师和运维团队这些年做的事情不计其数,主要包括这些大的初创公司:大部分关键系统都有自己多年来丰富的扩展和演进历史,包括会员图表服务(除了Leo之外)第一服务)、搜索(第二服务)、新闻种子、交流平台和会员信息后台。我们还构建了一个数据基础设施平台来支持长期增长。这是Databus和Kafka的第一次实战。后来,我们用Samza做数据流服务,用Espresso和Voldemort做存储方案,用Pinot做分析系统,还有其他定制方案。此外,我们的工具也有所改进,比如工程师可以自动化部署这些基础设施。我们还开发了大量使用Hadoop和Voldemort数据进行智能分析的离线工作流,例如“你可能认识的人”、“相似经历”、“感兴趣的校友”和“简历浏览地图”。我们重新思考了前端的做法,在混合页面(个人中心,我的大学页面)中加入了客户端模板,让应用程序可以更具交互性,只要请求JSON或部分JSON数据即可。此外,模板页面由CDN和浏览器缓存。我们还开始使用BigPipe和Play框架,将我们的模型从线程服务器更改为非阻塞异步服务器。在代码之外,我们使用Apache的多层代理和HAProxy来实现负载平衡、数据中心、安全、智能路由、服务器端渲染等。***,我们继续改进服务器的性能,包括优化硬件、内存和系统的高级优化,利用新的JRE。下一步LinkedIn今天仍然在快速增长,还有很多工作要做改进,我们正在解决一些问题,似乎只解决了部分问题——快来加入我们吧!
