LinkedIn 互联网架构扩展简史

news2024/11/17 5:25:58

LinkedIn成立于 2003 年,其目标是连接到您的网络以获得更好的工作机会。第一周只有 2,700 名会员。时间快进了很多年,LinkedIn 的产品组合、会员基础和服务器负载都取得了巨大的增长。

如今,LinkedIn 在全球运营,拥有超过 3.5 亿会员。我们每天每秒都会提供数以万计的网页。我们已经进入了移动时代,移动流量占全球流量的 50% 以上。所有这些请求都从我们的后端系统获取数据,而后端系统每秒处理数百万个查询。

那么,我们是如何到达那里的呢?

多年以前

就像今天许多网站一样,LinkedIn 最初是作为一个单一的整体应用程序完成这一切的。这个应用程序被称为 Leo。它托管所有不同页面的 Web servlet、处理业务逻辑并连接到一些 LinkedIn 数据库。

90e54f75584e23ccbdb876af79169eee.png
啊,网站开发的美好时光 - 美好而简单

成员图

作为社交网络要做的第一件事就是管理成员之间的连接。我们需要一个使用图形遍历查询连接数据并驻留在内存中的系统,以实现最高的效率和性能。由于这种不同的使用情况,很明显它需要独立于 Leo 进行扩展,因此我们的会员图表的一个名为 Cloud 的独立系统诞生了 - LinkedIn 的第一个服务。为了使该图服务与 Leo 分开,我们使用 Java RPC 进行通信。

大约在这个时候我们需要搜索功能。我们的会员图服务开始将数据输入到运行Lucene 的新搜索服务中。

副本读取数据库

随着网站的发展,Leo 也在不断发展,其角色和责任也不断增加,自然也增加了其复杂性。当多个 Leo 实例启动时,负载平衡很有帮助。但增加的负载给 LinkedIn 最关键的系统——其会员资料数据库带来了负担。

我们所做的一个简单的修复是经典的垂直扩展 - 投入更多的 CPU 和内存!虽然这赢得了一些时间,但我们需要进一步扩大规模。配置文件数据库同时处理读取和写入流量,因此为了扩展,引入了副本从属数据库。副本数据库是成员数据库的副本,使用最早版本的数据总线(现已开源)保持同步。它们被设置为处理所有读取流量,并构建逻辑来了解何时从副本读取相对于主主数据库是安全(一致)的。

aeed18058749276afa8482e711572572.png
* 虽然主从模型是中期解决方案,但我们已经转向分区数据库

随着网站的流量开始增加,我们的单一整体应用程序 Leo 经常在生产中出现故障,很难排除故障和恢复,也很难发布新代码。高可用性对于 LinkedIn 至关重要。很明显,我们需要“杀死 Leo”并将其分解为许多小型的功能性和无状态服务。

1db37c5c322a2a8137778af6d4116064.png
“杀死利奥”多年来一直是公司内部的口头禅……

面向服务的架构

工程部门开始提取微服务来保存 API 和业务逻辑,例如我们的搜索、个人资料、通信和群组平台。后来,我们的表示层被提取用于招聘人员产品或公共档案等领域。对于新产品,全新服务是在 Leo 之外创建的。随着时间的推移,每个功能区域都出现了垂直堆栈。

我们构建了前端服务器来从不同域获取数据模型、处理表示逻辑并构建 HTML(通过 JSP)。我们构建了中间层服务来提供对数据模型的 API 访问,并构建后端数据服务来提供对其数据库的一致访问。到 2010 年,我们已经拥有 150 多个独立服务。如今,我们拥有超过 750 项服务。

3537322fcbb43401b79c6f7d298fcf7e.png
LinkedIn 中面向服务的多层架构示例

由于无状态,可以通过启动任何服务的新实例并在它们之间使用硬件负载平衡器来实现扩展。我们积极开始对每个服务进行红线调整,以了解它可以承受多少负载,并构建了早期配置和性能监控功能。

缓存

LinkedIn 正在经历高速增长,需要进一步扩大规模。我们知道可以通过添加更多层缓存来完全减少负载。许多应用程序开始引入中间层缓存层,例如memcache或couchbase。我们还在数据层中添加了缓存,并在适当的时候开始使用带有预先计算结果的Voldemort。

随着时间的推移,我们实际上删除了许多中间层缓存。中间层缓存存储来自多个域的派生数据。虽然缓存一开始看起来是一种减少负载的简单方法,但失效和调用图的复杂性却变得失控。使缓存尽可能靠近数据存储可以保持较低的延迟,使我们能够水平扩展并减少认知负载。

Kafka

为了收集不断增长的数据量,LinkedIn 开发了许多用于流式传输和排队数据的自定义数据管道。例如,我们需要将数据流入数据仓库,我们需要将批量数据发送到我们的Hadoop 工作流程中进行分析,我们收集并聚合每个服务的日志,我们收集页面浏览量等跟踪事件,我们需要对 inMail 消息进行排队系统,每当有人更新个人资料时,我们都需要使我们的人员搜索系统保持最新状态。

随着网站的发展,更多的定制管道出现了。随着站点需要扩展,每个单独的管道也需要扩展。必须付出一些东西。结果是我们的分布式发布-订阅消息平台Kafka的开发。Kafka 成为一个通用管道,围绕提交日志的概念构建,并且在构建时考虑了速度和可扩展性。它使我们能够近乎实时地访问任何数据源,增强我们的 Hadoop 作业能力,使我们能够构建实时分析,极大地提高我们的站点监控和警报能力,并使我们能够可视化和跟踪我们的调用图。如今,Kafka每天处理超过5000 亿个事件。

9691b0c7ccc76d7b4f5e6bc20575fae4.png
Kafka 作为通用数据流代理

反转

规模化可以从多个维度来衡量,包括组织。2011 年底,LinkedIn 启动了一项名为Inversion的内部计划。这一举措暂停了功能开发,使整个工程组织能够专注于改进工具和部署、基础设施和开发人员的生产力。它成功地实现了我们构建当今可扩展新产品所需的工程敏捷性。

近代

当我们从 Leo 转型为面向服务的架构时,我们提取的 API 假设是基于 Java 的 RPC,跨团队不一致,与表示层紧密耦合,而且情况只会变得更糟。为了解决这个问题,我们构建了一个名为Rest.li的新 API 模型。Rest.li 是我们向以数据模型为中心的架构迈进的一步,它确保了整个公司一致的无状态 Restful API 模型。

通过使用 HTTP 上的 JSON,我们的新 API 最终使非基于 Java 的客户端变得容易。今天的 LinkedIn 仍然主要是一家 Java 商店,但也有许多使用 Python、Ruby、Node.js 和 C++ 的客户,这些都是内部开发的以及我们收购的技术堆栈。远离 RPC 还使我们摆脱了与表示层的高耦合和许多向后兼容性问题。另外,通过将动态发现 (D2)与 Rest.li 结合使用,我们获得了每个服务 API 的基于自动化客户端的负载平衡、发现和可扩展性。

如今,LinkedIn 在我们的所有数据中心拥有超过 975 个 Rest.li 资源,每天有超过 1000 亿次 Rest.li 调用。

421fd6b09cc3565f63e68549af32ea9a.png
Rest.li R2/D2 技术堆栈

超级积木

面向服务的架构可以很好地解耦域并独立扩展服务。但也有缺点。我们的许多应用程序获取多种类型的不同数据,进而进行数百个下游调用。在考虑所有许多下游调用时,这通常称为“调用图”或“扇出”。例如,任何个人资料页面请求获取的不仅仅是个人资料数据,还包括照片、连接、群组、订阅信息、关注信息、长篇博客文章、图表中的连接度、推荐等。此调用图可能难以管理并且变得越来越不守规矩。

我们引入了超级块的概念 - 具有单一访问 API 的后端服务分组。这使我们能够让特定的团队优化该块,同时检查每个客户端的调用图。

多数据中心

作为一家会员数量快速增长的跨国公司,我们需要扩大规模,超越从一个数据中心提供流量服务的范围。我们几年前就开始努力解决这个问题,首先是通过两个数据中心(洛杉矶和芝加哥)提供公共资料。一旦经过验证,我们就开始增强我们的所有服务,以处理数据复制、来自不同来源的回调、单向数据复制事件以及将用户固定到地理位置接近的数据中心。

我们的许多数据库都在Espresso(一种新的内部多租户数据存储)上运行。Espresso 在构建时就考虑到了多数据中心。它提供主/主支持并处理许多困难的复制。

多个数据中心对于维持“站点正常运行”和高可用性非常重要。您不仅需要避免每个单独服务的任何单点故障,还需要避免整个站点的任何单点故障。如今,LinkedIn 拥有三个主要数据中心,并在全球各地设有其他PoP 。

53bd1b63888368070f3f6741cd9b2464.png
LinkedIn 截至 2015 年的运营设置(圆圈代表数据中心,菱形代表 PoP)

我们还做了什么?

当然,我们的扩展故事从来没有这么简单。多年来,我们在所有工程和运营团队中做了无数的事情,其中包括一些更大的举措:

我们的许多最关键的系统都有自己丰富的历史和多年来解决规模问题的演变。这包括我们的会员图表服务(我们在 Leo 之外的第一项服务)、搜索(我们的第二项服务)、新闻源、通信平台和会员资料后端。

我们构建了能够实现长期增长的数据基础设施。这首先在 Databus 和 Kafka 中表现得很明显,然后在用于数据流的Samza 、用于存储解决方案的Espresso和 Voldemort、用于我们的分析系统的Pinot以及其他定制解决方案中得到延续。另外,我们的工具已经改进,开发人员可以自动配置此基础设施。

我们使用Hadoop和Voldemort 数据存储开发了一个大规模的离线工作流程,以预先计算数据见解,例如您可能认识的人、相似的个人资料、著名校友和个人资料浏览地图。

我们重新考虑了我们的前端方法,将客户端模板添加到组合中(个人资料页面、大学页面)。这使得应用程序的交互性更强,要求我们的服务器仅发送 JSON 或部分 JSON。另外,模板会缓存在 CDN 和浏览器中。我们还开始使用BigPipe和Play 框架,将我们的模型从线程 Web 服务器更改为非阻塞异步模型。

除了应用程序代码之外,我们还使用 Apache Traffic Server 和 HAProxy 引入了多层代理来处理负载平衡、数据中心固定、安全性、智能路由、服务器端渲染等。

最后,我们通过优化硬件、高级内存和系统调整以及利用更新的 Java 运行时,继续提高服务器的性能。

下一步是什么

LinkedIn 继续快速发展,我们仍有大量工作可以改进。我们正在解决很少有人能够解决的问题。


随手关注或者”在看“,诚挚感谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1551981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Portainer的替代Dockge?又一个Docker Compose管理器?

Dockge:让Docker Compose管理触手可及,一图胜千言,轻松构建与管控您的容器服务栈!- 精选真开源,释放新价值。 概览 Docker,这一开放源代码的创新平台,旨在实现应用程序部署、扩展与运维的自动化…

术语技巧:如何格式化网页中的术语

术语是语言服务中的核心语言资产。快速处理英汉对照的术语是我们在翻译技术学习过程中需要掌握的必备技能。 通常,我们需要把在权威网站上收集到的术语放到word当中,调整正左右对齐的样式,便于打印学习或者转化为Excel表。 如何快速实现这一…

docker容器下部署hbase并在springboot中通过jdbc连接

我在windows的docker中部署了一个hbase服务,然后用springboot连接到此服务并访问数据。 详情可参考项目中的README.md。项目中提供了用于构建镜像的dockerfile,以及测试代码。 项目连接: https://gitee.com/forgot940629/hbase_phoenix_sprin…

可解释 AI 系统及其构建方式的实用指南——可解释AI实战(PyTorch版)

过去五年中,我们就见证了人工智能(Artifcial Intelligence,AI)领域的重大突破,特别是在图像识别、自然语言理解等领域,以及围棋等棋盘游戏领域。随着人工智能在医疗和金融等行业的广泛应用,它正在辅助人类做出关键的决…

蚂蚁庄园今日答案

蚂蚁庄园是一款爱心公益游戏,用户可以通过喂养小鸡,产生鸡蛋,并通过捐赠鸡蛋参与公益项目。用户每日完成答题就可以领取鸡饲料,使用鸡饲料喂鸡之后,会可以获得鸡蛋,可以通过鸡蛋来进行爱心捐赠。其中&#…

Java中有哪些容器(集合类)?

Java中的集合类主要由Collection和Map这两个接口派生而出,其中Collection接口又派生出三个子接 口,分别是Set、List、Queue。所有的Java集合类,都是Set、List、Queue、Map这四个接口的实现 类,这四个接口将集合分成了四大类&#…

iOS - Runtime-API

文章目录 iOS - Runtime-API1. Runtime应用1.1 字典转模型1.2 替换方法实现1.3 利用关联对象给分类添加属性1.4 利用消息转发机制,解决方法找不到的异常问题 2. Runtime-API2.1 Runtime API01 – 类2.1.1 动态创建一个类(参数:父类&#xff0…

Linux 进程信号:产生信号

目录 一、通过终端按键产生信号 1、signal()函数 2、核心转储 3、ulmit命令 二、调用系统函数向进程发信号 1、kill()函数 2、raise()函数 3、abort()函数 三、发送信号的过程 读端关闭、写端继续写入的情况 如何理解软件条件给进程发送信号: 四、软件条件产生信…

【Java - 框架 - Lombok】(1) 普通Java项目通过Lombok+Logback完成日志的创建使用 - 快速上手

普通Java项目通过"Lombok""Logback"完成日志的创建使用 - 快速上手&#xff1b; 步骤A 说明 创建"Maven"项目&#xff1b; 图片 步骤B 说明 添加相关依赖项&#xff1b; 图片 代码 <!-- "Lombok"依赖项--> <dependency>&…

Exception in thread “main“ com.fasterxml.jackson.databind.JsonMappingException:

问题&#xff1a;jaskson反序列化超出最大长度 Caused by: com.fasterxml.jackson.core.exc.StreamConstraintsException: String length (5043456) exceeds the maximum length (5000000) 场景&#xff1a;前端传递过大base64 原因&#xff1a; jaskon默认已经限制了最大长…

在Windows系统上安装多个 Nodejs

前言 在Windows系统安装Nodejs 在Windows系统上安装多个 Nodejs v14.16.1安装位置 D:\sde\nodejs\node-v14.16.1-win-x64 v16.20.2安装位置 D:\sde\nodejs\node-v16.20.2-win-x64 v18.20.0安装位置 D:\sde\nodejs\node-v18.20.0-win-x64 v20.12.0安装位置 D:\sde\nod…

TTS 文本转语音模型综合简述

本文参考文献&#xff1a; [1] Kaur N, Singh P. Conventional and contemporary approaches used in text ot speech synthesis: A review[J]. Artificial Intelligence Review, 2023, 56(7): 5837-5880. [2] TTS | 一文了解语音合成经典论文/最新语音合成论文篇【20240111更新…

螺旋矩阵的算法刷题

螺旋矩阵的算法刷题 本文主要涉及螺旋矩阵的算法 包括三个题目分别是 59. 螺旋矩阵 II54. 螺旋矩阵 中等LCR 146. 螺旋遍历二维数组 文章目录 螺旋矩阵的算法刷题一 、螺旋矩阵简单1.1 实现一&#xff08;我认为这个方法更巧妙&#xff01;&#xff01;&#xff09;1.2 实现二&…

谷歌seo外推是什么?

​针对谷歌&#xff0c;站外推广是个不可忽视的环节&#xff0c;外推&#xff0c;也就是站外推广&#xff0c;就是所有在你的网站之外发生的活动&#xff0c;都是为了提升你的品牌在谷歌搜索结果中的排名&#xff0c;但其实本质依旧是外链&#xff0c;也就是指向你网站的链接&a…

【JavaScript】数组 ② ( JavaScript 数组索引 | JavaScript 遍历数组 | 使用 for 循环遍历数组 )

文章目录 一、JavaScript 数组索引1、数组索引2、数组索引 - 代码示例 二、JavaScript 遍历数组1、使用 for 循环遍历数组2、使用 for 循环遍历数组 - 代码示例 一、JavaScript 数组索引 1、数组索引 在 JavaScript 中 , 数组 的 " 索引 " 又称为 " 下标 "…

NVIDIA H200 创下 MLPerf LLM 最新推理记录

NVIDIA H200 Tensor Core GPU 和 NVIDIA TensorRT-LLM 创下 MLPerf LLM 最新推理记录 生成式人工智能正在解锁新的计算应用程序&#xff0c;通过持续的模型创新来极大地增强人类的能力。 生成式 AI 模型&#xff08;包括大型语言模型 (LLM)&#xff09;用于制作营销文案、编写计…

NOIP,CSP-J,CSP-S——树

一、树 概念: 节点、深度、路径、边 树的直径 真题: 答案:B 答案:A 一个树的边是n-1 现在是m,所以m-(n-1)=m-n+1

C++基础11:模板与命名空间

此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C从入门到深入的专栏&#xff0c;参考书籍&#xff1a;《深入浅出 C {\rm C} C》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C精通面向对象编程》(曾凡锋等)。 10.模板与命名空间 10.1 模板简述 模板使函数和类的处理对象…

Hbase 王者荣耀数据表 HBase常用Shell命令

大数据课本&#xff1a; HBase常用Shell命令 在使用具体的Shell命令操作HBase数据之前&#xff0c;需要首先启动Hadoop&#xff0c;然后再启动HBase&#xff0c;并且启动HBase Shell&#xff0c;进入Shell命令提示符状态&#xff0c;具体命令如下&#xff1a; $ cd /usr/local…