mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-16 06:03:45 +08:00
mod
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
<audio id="audio" title="04 | 分布式系统(上):学会用服务等级协议SLA来评估你的系统" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/37/94/3703b3b131044f4ff285ec07e6ddbf94.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
从今天开始,我们进入专栏的第二模块。通过这一模块的学习,带你一起夯实大规模数据处理的基础。
|
||||
|
||||
首先,我将结合硅谷顶尖科技公司的**最佳实践** (Best Practice) ,和你一起分享在设计分布式系统架构时,我们有可能会碰到哪些雷区?又有哪些必备的基础知识?
|
||||
|
||||
在硅谷一线大厂所维护的系统服务中,我们经常可以看见SLA这样的承诺。
|
||||
|
||||
例如,在谷歌的云计算服务平台Google Cloud Platform中,他们会写着“99.9% Availability”这样的承诺。那什么是“99.9% Availability”呢?
|
||||
|
||||
要理解这个承诺是什么意思,首先,你需要了解到底什么是SLA?
|
||||
|
||||
SLA(Service-Level Agreement),也就是**服务等级协议**,指的是系统服务提供者(Provider)对客户(Customer)的一个服务承诺。这是衡量一个大型分布式系统是否“健康”的常见方法。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/77/be/77361f3533c4579bb0d9661af49616be.jpg" alt="">
|
||||
|
||||
在开发设计系统服务的时候,无论面对的客户是公司外部的个人、商业用户,还是公司内的不同业务部门,我们都应该对自己所设计的系统服务有一个定义好的SLA。
|
||||
|
||||
因为SLA是一种服务承诺,所以指标可以多种多样。根据我的实践经验,给你介绍最常见的四个SLA指标,可用性、准确性、系统容量和延迟。
|
||||
|
||||
## 1. 可用性(Availabilty)
|
||||
|
||||
可用性指的是系统服务能正常运行所占的时间百分比。
|
||||
|
||||
如果我们搭建了一个拥有“100%可用性”的系统服务,那就意味着这个系统在任何时候都能正常运行。是不是很完美?但真要实现这样的目标其实非常困难,并且成本也会很高。
|
||||
|
||||
我们知道,即便是大名鼎鼎的亚马逊AWS云计算服务这样大型的、对用户来说极为关键的系统,也不能承诺100%的可用性,它的系统服务从推出到现在,也有过服务中断(Service Outage)的时候。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a7/1a/a7d6bd2401677aca88c0623aa04d861a.jpg" alt="">
|
||||
|
||||
对于许多系统而言,四个9的可用性(99.99% Availability,或每年约50分钟的系统中断时间)即可以被认为是**高可用性**(High availability)。
|
||||
|
||||
说到这里,我来为你揭开一开始所提到的“99.9% Availability”的真实含义。
|
||||
|
||||
“99.9% Availability”指的是一天当中系统服务将会有大约86秒的服务间断期。服务间断也许是因为系统维护,也有可能是因为系统在更新升级系统服务。
|
||||
|
||||
86秒这个数字是怎么算出来的呢?
|
||||
|
||||
99.9%意味着有0.1%的可能性系统服务会被中断,而一天中有24小时 × 60分钟 × 60秒,也就是有(24 × 60 × 60 × 0.001) = 86.4秒的可能系统服务被中断了。而上面所说的四个9的高可用性服务就是承诺可以将一天当中的服务中断时间缩短到只有(24 × 60 × 60 × 0.0001) = 8.64秒。
|
||||
|
||||
## 2. 准确性(Accuracy)
|
||||
|
||||
准确性指的是我们所设计的系统服务中,是否允许某些数据是不准确的或者是丢失了的。如果允许这样的情况发生,用户可以接受的概率(百分比)是多少?
|
||||
|
||||
这该怎么衡量呢?不同的系统平台可能会用不同的指标去定义准确性。很多时候,系统架构会以**错误率**(Error Rate)来定义这一项SLA。
|
||||
|
||||
怎么计算错误率呢?可以用导致系统产生内部错误(Internal Error)的有效请求数,除以这期间的有效请求总数。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/16/26/16c92cc68b462d469fb535aaa08b8d26.jpg" alt="">
|
||||
|
||||
例如,我们在一分钟内发送100个有效请求到系统中,其中有5个请求导致系统返回内部错误,那我们可以说这一分钟系统的错误率是 5 / 100 = 5%。
|
||||
|
||||
下面,我想带你看看硅谷一线公司所搭建的架构平台的准确性SLA。
|
||||
|
||||
Google Cloud Platform的SLA中,有着这样的准确性定义:每个月系统的错误率超过5%的时间要少于0.1%,以每分钟为单位来计算。
|
||||
|
||||
而亚马逊AWS云计算平台有着稍微不一样的准确性定义:以每5分钟为单位,错误率不会超过0.1%。
|
||||
|
||||
你看,我们可以用错误率来定义准确性,但具体该如何评估系统的准确性呢?一般来说,我们可以采用**性能测试**(Performance Test)或者是**查看系统日志**(Log)两种方法来评估。
|
||||
|
||||
具体的做法我会在后面展开讲解,今天你先理解这项指标就可以了。
|
||||
|
||||
## 3. 系统容量(Capacity)
|
||||
|
||||
在数据处理中,系统容量通常指的是**系统能够支持的预期负载量是多少**,一般会以每秒的请求数为单位来表示。
|
||||
|
||||
我们常常可以看见,某个系统的架构可以处理的QPS (Queries Per Second)是多少又或者RPS(Requests Per Second)是多少。这里的QPS或者是RPS就是指系统每秒可以响应多少请求数。
|
||||
|
||||
我们来看看之前Twitter发布的一项数据,Twitter系统可以响应30万的QPS来读取Twitter Timelines。这里Twitter系统给出的就是他们对于系统容量(Capacity)的SLA。
|
||||
|
||||
你可能会问,我要怎么给自己设计的系统架构定义出准确的QPS呢?以我的经验看,可以有下面这几种方式。
|
||||
|
||||
第一种,是使用**限流**(Throttling)的方式。
|
||||
|
||||
如果你是使用Java语言进行编程的,就可以使用Google Guava库中的RateLimiter类来定义每秒最多发送多少请求到后台处理。
|
||||
|
||||
假设我们在每台服务器都定义了一个每秒最多处理1000个请求的RateLimiter,而我们有N台服务器,在最理想的情况下,我们的QPS可以达到1000 * N。
|
||||
|
||||
这里要注意的雷区是,这个请求数并不是设置得越多越好。因为每台服务器的内存有限,过多的请求堆积在服务器中有可能会导致**内存溢出**(Out-Of-Memory)的异常发生,也就是所有请求所需要占用的内存超过了服务器能提供的内存,从而让整个服务器崩溃。
|
||||
|
||||
第二种,是在系统交付前进行**性能测试**(Performance Test)。
|
||||
|
||||
我们可以使用像Apache JMeter又或是LoadRunner这类型的工具对系统进行性能测试。这类工具可以测试出系统在峰值状态下可以应对的QPS是多少。
|
||||
|
||||
当然了,这里也是有雷区的。
|
||||
|
||||
有的开发者可能使用同一类型的请求参数,导致后台服务器在多数情况下**命中缓存**(Cache Hit)。这个时候得到的QPS可能并不是真实的QPS。
|
||||
|
||||
打个比方,服务器处理请求的正常流程需要查询后台数据库,得到数据库结果后再返回给用户,这个过程平均需要1秒。在第一次拿到数据库结果后,这个数据就会被保存在缓存中,而如果后续的请求都使用同一类型的参数,导致结果不需要从数据库得到,而是直接从缓存中得到,这个过程我们假设只需要0.1秒。那这样,我们所计算出来的QPS就会比正常的高出10倍。所以在生成请求的时候,要格外注意这一点。
|
||||
|
||||
第三种,是分析系统在实际使用时产生的**日志**(Log)。
|
||||
|
||||
系统上线使用后,我们可以得到日志文件。一般的日志文件会记录每个时刻产生的请求。我们可以通过系统每天在最繁忙时刻所接收到的请求数,来计算出系统可以承载的QPS。
|
||||
|
||||
不过,这种方法不一定可以得到系统可以承载的最大QPS。
|
||||
|
||||
在这里打个比喻,一家可以容纳上百桌客人的餐馆刚开业,因为客流量还比较小,在每天最繁忙的时候只接待了10桌客人。那我们可以说这家餐馆最多只能接待10桌客人吗?不可以。
|
||||
|
||||
同样的,以分析系统日志的方法计算出来的QPS并不一定是服务器能够承载的最大QPS。想要得到系统能承受的最大QPS,更多的是性能测试和日志分析相结合的手段。
|
||||
|
||||
## 4. 延迟(Latency)
|
||||
|
||||
延迟指的是**系统在收到用户的请求到响应这个请求之间的时间间隔**。
|
||||
|
||||
在定义延迟的SLA时,我们常常看到系统的SLA会有p95或者是p99这样的延迟声明。这里的p指的是percentile,也就是百分位的意思。如果说一个系统的p95 延迟是1秒的话,那就表示在100个请求里面有95个请求的响应时间会少于1秒,而剩下的5个请求响应时间会大于1秒。
|
||||
|
||||
下面我们用一个具体的例子来说明延迟这项指标在SLA中的重要性。
|
||||
|
||||
假设,我们已经设计好了一个社交软件的系统架构。这个社交软件在接收到用户的请求之后,需要读取数据库中的内容返回给用户。
|
||||
|
||||
为了降低系统的延迟,我们会将数据库中内容放进缓存(Cache)中,以此来减少数据库的读取时间。在系统运行了一段时间后,我们得到了一些缓存命中率(Cache Hit Ratio)的信息。有90%的请求命中了缓存,而剩下的10%的请求则需要重新从数据库中读取内容。
|
||||
|
||||
这时服务器所给我们的p95或者p99延迟恰恰就衡量了系统的最长时间,也就是从数据库中读取内容的时间。作为一个优秀架构师,你可以通过改进缓存策略从而提高缓存命中率,也可以通过优化数据库的Schema或者索引(Index)来降低p95或p99 延迟。
|
||||
|
||||
总而言之,当p95或者p99过高时,总会有5%或者1%的用户抱怨产品的用户体验太差,这都是我们要通过优化系统来避免的。
|
||||
|
||||
## 小结
|
||||
|
||||
通过今天的内容,你可以发现,定义好一个系统架构的SLA对于一个优秀的架构师来说是必不可少的一项技能,也是一种基本素养。
|
||||
|
||||
特别是当系统架构在不停迭代的时候,有了一个明确的SLA,我们可以知道下一代系统架构的改进目标以及优化好的系统架构是否比上一代的系统SLA更加优秀。
|
||||
|
||||
我们通常会使用可用性、准确性、系统容量、延迟这四个指标来定义系统架构的SLA。
|
||||
|
||||
## 思考题
|
||||
|
||||
你可以思考一下,在自己所在的开发项目中,系统的SLA是什么呢?又有什么方面可以优化的呢?
|
||||
|
||||
欢迎你把答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<audio id="audio" title="05 | 分布式系统(下):架构师不得不知的三大指标" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/c0/03/c038f265fa20ee6f24136e74b9065403.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
上一讲中,我们学习了如何用服务等级协议(SLA)来评估我们设计的分布式系统,并了解了几个常见的SLA指标。
|
||||
|
||||
今天我们继续来探索分布式系统的另外几个重要基础概念。
|
||||
|
||||
## 可扩展性
|
||||
|
||||
还是从我们为什么需要分布式系统讲起。原因是我们要面对的数据量越来越大,从GB到TB再到现在的PB级,单机无法胜任这样的工作。
|
||||
|
||||
工作中也常有这样的场景,随着业务变得越来越复杂,之前设计的系统无法处理日渐增长的负载。这时,我们就需要增加系统的容量。
|
||||
|
||||
分布式系统的核心就是可扩展性(Scalability)。
|
||||
|
||||
最基本而且最流行的增加系统容量的模型有两种: 水平扩展(Horizontal Scaling)和垂直扩展(Vertical Scaling)。
|
||||
|
||||
所谓水平扩展,就是指在现有的系统中增加新的机器节点。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2f/4b/2fed13c9e11ae3c72d1b0b66809c3f4b.jpg" alt="">
|
||||
|
||||
垂直扩展就是在不改变系统中机器数量的情况下,“升级”现有机器的性能,比如增加机器的内存。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/75/01/75ca153b40ff6db8b424399cb7d3a601.jpg" alt="">
|
||||
|
||||
举个例子,假设你现在负责一批木材采伐的操作。你有3辆卡车,每辆车一次可以运25根木材。那么1小时最多可以运3辆卡车 * 25根木材 * 1小时=75根木材/小时。
|
||||
|
||||
如果要使这个系统的负荷量增加一倍,用水平扩展的办法,我们可以将卡车的数量增加到6辆;用垂直扩展的办法,我们可以使每辆卡车的运输量增加一倍,或者使每辆卡车的速度增加一倍。
|
||||
|
||||
你是不是已经发现了,水平扩展的适用范围更广,操作起来更简单,并且会提升系统的可用性(Availability)。
|
||||
|
||||
如果你的系统部署在AWS或者其他主流的云服务上,你只需要点几个按钮,就可以在现有的机器集群中增加一个新的节点。
|
||||
|
||||
但是,无节制地增加机器数量也会带来一些问题,比如机器的管理、调度、通信会变得更加复杂,出错的可能性会更高,更难保证数据的一致性等等。
|
||||
|
||||
与之相反,垂直扩展并没有让整个系统变得更加复杂,控制系统的代码也不需要做任何调整,但是它受到的限制比较多。多数情况下,单个机器的性能提升是有限的。而且受制于摩尔定律,提高机器的性能往往比购买新的机器更加昂贵。
|
||||
|
||||
所以在工作中,我们要对这两种模式进行取舍,要具体情况具体分析。
|
||||
|
||||
同样地,在大数据的时代,数据增长速度越来越快,数据规模越来越大,对数据存储系统的扩展性要求也越来越高。
|
||||
|
||||
传统的关系型数据库因为表与表之间的数据有关联,经常要进行Join操作,所有数据要存放在单机系统中,很难支持水平扩展。而NoSQL型的数据库天生支持水平扩展,所以这类存储系统的应用越来越广,如BigTable、MongoDB和Redis等。
|
||||
|
||||
## 一致性
|
||||
|
||||
可用性对于任何分布式系统都很重要。一般来说,构成分布式系统的机器节点的可用性要低于系统的可用性。
|
||||
|
||||
举个例子,如果我们想要构建一个可用性99.999%的分布式系统(每年约5分钟的宕机时间),但是我们使用的单台机器节点的可用性是99.9%(每年约8个小时的宕机时间)。那么想要达到我们的目标,最简单的办法就是增加系统中机器节点的数量。这样即使有部分机器宕机了,其他的机器还在持续工作,所以整个系统的可用性就提高了。
|
||||
|
||||
这种情况下,我们要思考一个问题:如何保证系统中不同的机器节点在同一时间,接收到和输出的数据是一致的呢?这时就要引入一致性(Consistency)的概念。
|
||||
|
||||
回到之前的例子,要保证分布式系统内的机器节点有相同的信息,就需要机器之间定期同步。
|
||||
|
||||
然而,发送信息也会有失败的可能,比如信息丢失或者有的节点正好宕机而无法接收。因此,一致性在高可用性的系统里是非常核心的概念。
|
||||
|
||||
接下来,我会给你介绍几个在工程中常用的一致性模型,分别是:强一致性(Strong Consistency),弱一致性(Weak Consistency),最终一致性(Eventual Consistency)。
|
||||
|
||||
- 强一致性:系统中的某个数据被成功更新后,后续任何对该数据的读取操作都将得到更新后的值。所以在任意时刻,同一系统所有节点中的数据是一样的。
|
||||
- 弱一致性:系统中的某个数据被更新后,后续对该数据的读取操作可能得到更新后的值,也可能是更改前的值。但经过“不一致时间窗口”这段时间后,后续对该数据的读取都是更新后的值。
|
||||
- 最终一致性:是弱一致性的特殊形式。存储系统保证,在没有新的更新的条件下,最终所有的访问都是最后更新的值。
|
||||
|
||||
上面这三点我描述得比较正式,但其实都不难理解。这里,我进一步给你做个说明。
|
||||
|
||||
在强一致性系统中,只要某个数据的值有更新,这个数据的副本都要进行同步,以保证这个更新被传播到所有备份数据库中。在这个同步进程结束之后,才允许服务器来读取这个数据。
|
||||
|
||||
所以,强一致性一般会牺牲一部分延迟性,而且对于全局时钟的要求很高。举个例子,Google Cloud的Cloud Spanner就是一款具备强一致性的全球分布式企业级数据库服务。
|
||||
|
||||
在最终一致性系统中,我们无需等到数据更新被所有节点同步就可以读取。尽管不同的进程读同一数据可能会读到不同的结果,但是最终所有的更新会被按时间顺序同步到所有节点。所以,最终一致性系统支持异步读取,它的延迟比较小。比如亚马逊云服务的DynamoDB就支持最终一致的数据读取。
|
||||
|
||||
除了以上三个,分布式系统理论中还有很多别的一致性模型,如顺序一致性(Sequential Consistency),因果一致性(Casual Consistency)等,如果你感兴趣的话,可以自己查资料了解一下。
|
||||
|
||||
在实际应用系统中,强一致性是很难实现的,应用最广的是最终一致性。我们一起来看两个例子。
|
||||
|
||||
很多人认为银行间转账应该是强一致的。但是你仔细分析一下就会发现,事实并非如此。
|
||||
|
||||
举个例子,小王给小张转账1000元,小王的账户扣除了1000,此时小张并不一定立刻就收到1000元。这里可能会存在一个不一致的时间窗口:小王的钱扣除了1000元,小张还没收到1000元的时候。
|
||||
|
||||
另外一个例子,在12306网站买票的功能,也不是强一致的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/73/c5/735f1868884bb30afd2e9b1009c398c5.jpg" alt="">
|
||||
|
||||
如果你在12306上发现一趟列车还剩余10张车票,你发起请求订了一张票,系统给你返回的可能是“正在排队,剩余10张票,现在有15人在购买”。
|
||||
|
||||
这时,你可能就需要去查询未完成订单,因为系统并没有给你及时返回订票成功或失败的结果。如果有人退了一张票,这张票也不会立即返回到票池中。这里明显也存在不一致的时间窗口。
|
||||
|
||||
但是,最终10张票只会卖给10个人,不可能卖给11个人,这就是最终一致性所谓的“最终所有数据都会同步”。
|
||||
|
||||
讲到这里,你对分布式系统的扩展性和一致性就很清楚了吧?接下来再给你介绍一个重要概念。
|
||||
|
||||
## 持久性
|
||||
|
||||
数据持久性(Data Durability)意味着数据一旦被成功存储就可以一直继续使用,即使系统中的节点下线、宕机或数据损坏也是如此。
|
||||
|
||||
不同的分布式数据库拥有不同级别的持久性。有些系统支持机器/节点级别的持久性,有些做到了集群级别,而有些系统压根没有持久性。
|
||||
|
||||
想要提高持久性,数据复制是较为通用的做法。因为把同一份数据存储在不同的节点上,即使有节点无法连接,数据仍然可以被访问。
|
||||
|
||||
在分布式数据处理系统中,还有一个持久性概念是消息持久性。什么意思呢?在分布式系统中,节点之间需要经常相互发送消息去同步以保证一致性。对于重要的系统而言,常常不允许任何消息的丢失。
|
||||
|
||||
分布式系统中的消息通讯通常由分布式消息服务完成,比如RabbitMQ、Kafka。这些消息服务能支持(或配置后支持)不同级别的消息送达可靠性。消息持久性包含两个方面:
|
||||
|
||||
1. 当消息服务的节点发生了错误,已经发送的消息仍然会在错误解决之后被处理;
|
||||
1. 如果一个消息队列声明了持久性,那么即使队列在消息发送之后掉线,仍然会在重新上线之后收到这条消息。
|
||||
|
||||
## 小结
|
||||
|
||||
在这一讲中,我们探讨了分布式处理系统的三个重要指标:扩展性,一致性和持久性。
|
||||
|
||||
结合前边提到的延迟性、可用性以及准确性,我们不难发现,这些设计分布式系统所要考虑的量化指标存在一定程度上的冲突。不可能有一个分布式处理系统在不牺牲某一指标的前提下,让每一个指标都达到最好。
|
||||
|
||||
作为优秀的系统架构师,我们一定要学会具体情况具体分析,找到最适合自己系统的指标,适当做出取舍。但是这一点说起来容易做起来难,到底该怎么取舍呢?你可以先思考一下这个问题,下一讲中我会结合CAP定理和你进一步讨论。
|
||||
|
||||
## 思考题
|
||||
|
||||
对于微信朋友圈的评论功能,你觉得哪种一致性模型更适用?为什么?
|
||||
|
||||
欢迎你把答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。
|
||||
|
||||
|
||||
129
极客时间专栏/大规模数据处理实战/模块二 | 实战学习大规模数据处理基本功/06 | 如何区分批处理还是流处理?.md
Normal file
129
极客时间专栏/大规模数据处理实战/模块二 | 实战学习大规模数据处理基本功/06 | 如何区分批处理还是流处理?.md
Normal file
@@ -0,0 +1,129 @@
|
||||
<audio id="audio" title="06 | 如何区分批处理还是流处理?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/16/77/16c7b3c6e3d19ec2373f6d5c0b09f577.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
今天,我将会带领你一起学习在进行大规模数据处理时,无论如何也绕不开的两个处理模式:批处理(Batching Processing)和流处理(Streaming Processing)。
|
||||
|
||||
在我看来,大规模的视频流系统、大规模物联网(IoT)数据监控系统等各种现代大规模数据系统的出现,已经成为了一种必然的历史潮流。
|
||||
|
||||
无论你是在从事哪一种开发方向,都不可避免地要与这些海量数据打交道。如何能既满足实际应用场景的需求,又高效地处理好大规模数据,在整个项目开发架构中都是非常重要的一个环节。
|
||||
|
||||
在开始讲解批处理和流处理之前,我想先介绍一下几个必要的背景知识。
|
||||
|
||||
### 无边界数据和有边界数据
|
||||
|
||||
这个世界上的数据可以抽象成为两种,分别是无边界数据(Unbounded Data)和有边界数据(Bounded Data)。
|
||||
|
||||
顾名思义,**无边界数据**是一种不断增长,可以说是无限的数据集。
|
||||
|
||||
这种类型的数据,我们无法判定它们到底什么时候会停止发送。
|
||||
|
||||
例如,从手机或者从传感器发送出来的信号数据,又比如我们所熟知的移动支付领域中的交易数据。因为每时每刻都会有交易产生,所以我们不能判定在某一刻这类数据就会停止发送了。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/92/f4/923137d938e2f11b52a69d8446df81f4.jpg" alt="">
|
||||
|
||||
在国外的一些技术文章上,有时候我们会看到“流数据(Streaming Data)”这一说法,其实它和无边界数据表达的是同一个概念。
|
||||
|
||||
与此相反,**有边界数据**是一种有限的数据集。
|
||||
|
||||
这种数据更常见于已经保存好了的数据中。例如,数据库中的数据,或者是我们常见的CSV格式文件中的数据。
|
||||
|
||||
当然了,你可能会问,那我们把无边界数据按照时间窗口提取一小份出来,那这样的数据是什么数据呢?
|
||||
|
||||
拿我们之前提到过的移动支付中的交易数据来说吧。移动支付中的交易数据可以看作是无边界数据。那我们按2019年4月29日这个时间窗口提取出来的数据呢?这个当日的交易数据就变成了有边界数据了。
|
||||
|
||||
所以,有边界数据其实可以看作是无边界数据的一个子集。
|
||||
|
||||
### 事件时间和处理时间
|
||||
|
||||
在处理大规模数据的时候,我们通常还会关心**时域**(Time Domain)的问题。
|
||||
|
||||
我们要处理的任意数据都会有两种时域,分别是事件时间(Event Time)和处理时间(Precessing Time)。
|
||||
|
||||
**事件时间**指的是一个数据实际产生的时间点,而**处理时间**指的是处理数据的系统架构实际接收到这个数据的时间点。
|
||||
|
||||
下面我来用一个实际的例子进一步说明这两个时间概念。
|
||||
|
||||
现在假设,你正在去往地下停车场的路上,并且打算用手机点一份外卖。选好了外卖后,你就用在线支付功能付款了,这个时候是12点05分。恰好这时,你走进了地下停车库,而这里并没有手机信号。因此外卖的在线支付并没有立刻成功,而支付系统一直在重试(Retry)“支付”这个操作。
|
||||
|
||||
当你找到自己的车并且开出地下停车场的时候,已经是12点15分了。这个时候手机重新有了信号,手机上的支付数据成功发到了外卖在线支付系统,支付完成。
|
||||
|
||||
在上面这个场景中你可以看到,支付数据的事件时间是12点05分,而支付数据的处理时间是12点15分。事件时间和处理时间的概念,你明白了吗?
|
||||
|
||||
在了解完上面的4个基本概念后,我将开始为你揭开批处理和流处理模式的面纱。
|
||||
|
||||
### 批处理
|
||||
|
||||
数据的批处理,可以理解为一系列相关联的任务按顺序(或并行)一个接一个地执行。批处理的输入是在一段时间内已经收集保存好的数据。每次批处理所产生的输出也可以作为下一次批处理的输入。
|
||||
|
||||
绝大部分情况下,批处理的输入数据都是**有边界数据**,同样的,输出结果也一样是**有边界数据**。所以在批处理中,我们所关心的更多会是数据的**事件时间**。
|
||||
|
||||
举个例子,你在每年年初所看到的“支付宝年账单”就是一个数据批处理的典型例子。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/de/03/deb1cc0e27841807e28e8202a055d503.jpg" alt="">
|
||||
|
||||
支付宝会将我们在过去一年中的消费数据存储起来,并作为批处理输入,提取出过去一年中产生交易的事件时间,然后经过一系列业务逻辑处理,得到各种有趣的信息作为输出。
|
||||
|
||||
在许多情况下,批处理任务会被安排,并以预先定义好的时间间隔来运行,例如一天,一个月或者是一年这样的特定时间。
|
||||
|
||||
在银行系统中,银行信用卡消费账单和最低还款额度也都是由批处理系统以预先定义好的一个月的时间间隔运行,所产生出来的。
|
||||
|
||||
批处理架构通常会被设计在以下这些应用场景中:
|
||||
|
||||
<li>
|
||||
日志分析:日志系统是在一定时间段(日,周或年)内收集的,而日志的数据处理分析是在不同的时间内执行,以得出有关系统的一些关键性能指标。
|
||||
</li>
|
||||
<li>
|
||||
计费应用程序:计费应用程序会计算出一段时间内一项服务的使用程度,并生成计费信息,例如银行在每个月末生成的信用卡还款单。
|
||||
</li>
|
||||
<li>
|
||||
数据仓库:数据仓库的主要目标是根据收集好的数据事件时间,将数据信息合并为静态快照 (static snapshot),并将它们聚合为每周、每月、每季度的报告等。
|
||||
</li>
|
||||
|
||||
由Google MapReduce衍生出来的开源项目Apache Hadoop或者是Apache Spark等开源架构都是支持这种大数据批处理架构的。
|
||||
|
||||
由于完成批处理任务具有高延迟性,一般可以需要花费几小时,几天甚至是几周的时间。要是在开发业务中有快速响应用户的时间需求,我们则需要考虑使用流处理/实时处理来处理大数据。
|
||||
|
||||
### 流处理
|
||||
|
||||
数据的流处理可以理解为系统需要接收并处理一系列连续不断变化的数据。例如,旅行预订系统,处理社交媒体更新信息的有关系统等等。
|
||||
|
||||
流处理的输入数据基本上都是**无边界数据**。而流处理系统中是关心数据的事件时间还是处理时间,将视具体的应用场景而定。
|
||||
|
||||
例如,像网页监控系统这样的流处理系统要计算网站的QPS,它所关心的更多是**处理时间**,也就是网页请求数据被监控系统接收到的时间,从而计算QPS。
|
||||
|
||||
而在一些医疗护理监控系统的流处理系统中,他们则更关心数据的**事件时间**,这种系统不会因为接收到的数据有网络延时,而忽略数据本来产生的时间。
|
||||
|
||||
流处理的特点应该是要足够快、低延时,以便能够处理来自各种数据源的大规模数据。流处理所需的响应时间更应该以毫秒(或微秒)来进行计算。像我们平时用到的搜索引擎,系统必须在用户输入关键字后以毫秒级的延时返回搜索结果给用户。
|
||||
|
||||
流处理速度如此之快的根本原因是因为它在数据到达磁盘之前就对其进行了分析。
|
||||
|
||||
当流处理架构拥有在一定时间间隔(毫秒)内产生逻辑上正确的结果时,这种架构可以被定义为**实时处理**(Real-time Processing)。
|
||||
|
||||
而如果一个系统架构可以接受以分钟为单位的数据处理时间延时,我们也可以把它定义为**准实时处理**(Near real-time Processing)。
|
||||
|
||||
还记得我们在介绍批处理架构中所说到的不足吗?没错,是高延迟。而流处理架构则恰恰拥有高吞度量和低延迟等特点。
|
||||
|
||||
流处理架构通常都会被设计在以下这些应用场景中:
|
||||
|
||||
- 实时监控:捕获和分析各种来源发布的数据,如传感器,新闻源,点击网页等。
|
||||
- 实时商业智能:智能汽车,智能家居,智能病人护理等。
|
||||
- 销售终端(POS)系统:像是股票价格的更新,允许用户实时完成付款的系统等。
|
||||
|
||||
在如今的开源架构生态圈中,如Apache Kafka、Apache Flink、Apache Storm、Apache Samza等,都是流行的流处理架构平台。
|
||||
|
||||
在介绍完这两种处理模式后,你会发现,无论是批处理模式还是流处理模式,在现实生活中都有着很广泛的应用。你应该根据自己所面临的实际场景来决定到底采用哪种数据处理模式。
|
||||
|
||||
## 小结
|
||||
|
||||
批处理模式在不需要实时分析结果的情况下是一种很好的选择。尤其当业务逻辑需要处理大量的数据以挖掘更为深层次数据信息的时候。
|
||||
|
||||
而在应用需求需要对数据进行实时分析处理时,或者说当有些数据是永无止境的事件流时(例如传感器发送回来的数据时),我们就可以选择用流处理模式。
|
||||
|
||||
## 思考题
|
||||
|
||||
相信在学习完这一讲后,你会对批处理模式和流处理模式有着清晰的认识。今天的思考题是,在你的日常开发中,所面临的数据处理模式又是哪一种模式呢?
|
||||
|
||||
欢迎你把答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<audio id="audio" title="07 | Workflow设计模式:让你在大规模数据世界中君临天下" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/5a/94/5a07fa5ef2cd43cc74a419763df7c994.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
今天我要与你分享的主题是“Workflow设计模式”。
|
||||
|
||||
在上一讲中,我们一起学习了大规模数据处理的两种处理模式——批处理和流处理。
|
||||
|
||||
利用好这两种处理模式,作为架构师的你就可以运筹帷幄,根据实际需求搭建出一套符合自己应用的数据处理系统。
|
||||
|
||||
然而,光是掌握了这两种数据处理模式就足够自如应对大规模数据世界中的需求挑战吗?从我的实战经验中看来,其实未必。
|
||||
|
||||
我们每个人在最开始学习大规模数据处理的时候,可能都是以WordCount作为教学例子来进行学习的。
|
||||
|
||||
WordCount这个例子,只需要一个单词集合作为输入,数据处理的结果是统计单词出现的次数,中间只需要经过一次数据处理的转换,就如同下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1b/9c/1b82384d37d37653721613e49933359c.jpg" alt="">
|
||||
|
||||
但在现实的应用场景种中,各式各样的应用需求决定了大规模数据处理中的应用场景会比WordCount复杂很多倍。
|
||||
|
||||
我还是以我在第一讲中所提到过的例子来说明吧。
|
||||
|
||||
在根据活跃在街头的美团外卖电动车的数量来预测美团的股价这个例子中,我们的输入数据集有可能不止一个。
|
||||
|
||||
例如,会有自己团队在街道上拍摄到的美团外卖电动车图片,会有第三方公司提供的美团外卖电动车数据集等等。
|
||||
|
||||
整个数据处理流程又会需要至少10个处理模块,每一个处理模块的输出结果都将会成为下一个处理模块的输入数据,就如同下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/bb/a7/bb5bac6c66bca6c3d16172046a84e5a7.jpg" alt="">
|
||||
|
||||
像上面的图示一样,我们将这种由多个不同的处理模块连接在一起,最后得出一个自己需要结果的有向无环图(Directed Acyclic Graph/DAG),称为一个工作流系统(Workflow System)。
|
||||
|
||||
在工作流系统的每个处理模块里,系统要执行的操作有可能不是单单一个数据转换的操作这么简单。像在上面例子的Ingestion这个模块中,我们需要将多个不同的数据集合并在一起,也需要将不合格的一些图片过滤掉。
|
||||
|
||||
如果你用过Apache Spark 1.4以上的版本的话,Spark平台里面的Execution DAG就可以为你展示一个完整的工作流图。
|
||||
|
||||
今天,我为你解释四种工作流系统的设计模式,希望你能够很好地理解它们,并运用在自己的数据处理系统设计中。在遇到各种复杂的应用场景的时候能够从容面对。
|
||||
|
||||
## 复制模式(Copier Pattern)
|
||||
|
||||
复制模式通常是将单个数据处理模块中的数据,完整地复制到两个或更多的数据处理模块中,然后再由不同的数据处理模块进行处理。工作流系统图通常如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/5f/3b/5fa7f641e5d2fd2ca79644c3e3a04f3b.jpg" alt="">
|
||||
|
||||
当我们在处理大规模数据时,需要对同一个数据集采取多种不同的数据处理转换,我们就可以优先考虑采用**复制模式**。
|
||||
|
||||
我来举个在YouTube视频平台中,系统处理视频数据集的一个例子吧。
|
||||
|
||||
我们都知道,视频平台很多时候都会提供不同分辨率的视频。4K或1080P的视频可以提供给网络带宽很高的用户。而在网络很慢的情况下,视频平台系统会自动转换成低分辨率格式的视频,像360P这样的视频给用户。
|
||||
|
||||
而在YouTube视频平台中,如果你将鼠标放在视频缩略图上,它会自动播放一段已经生成好的动画缩略图(Animated GIF Thumbnail )。
|
||||
|
||||
不仅如此,在平台的背后,一个视频的数据集可能被自然语言理解(NLP)的数据处理模块分析,用以自动生成视频字幕;还有可能被视频分析的数据处理模块分析,用以产生更好的内容推荐系统。那么,它的整个工作流系统就会如下图所示一样。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/33/b9/3353788330d777c427d07d2b1d65e5b9.jpg" alt="">
|
||||
|
||||
我们可以看到,在这个工作流系统中,每个数据处理模块的输入是相同的,而下面的5个数据处理模块都可以单独并且同步地运行处理。
|
||||
|
||||
## 过滤模式(Filter Pattern)
|
||||
|
||||
过滤模式的作用是过滤掉不符合特定条件的数据。
|
||||
|
||||
在数据集通过了这个数据处理模块后,数据集会缩减到只剩下符合条件的数据。工作流系统图通常如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2e/6c/2ed81b389597b6de86300ef19f95bb6c.jpg" alt="">
|
||||
|
||||
当我们在处理大规模数据时,需要针对一个数据集中某些特定的数据采取数据处理时,我们就可以优先考虑采用**过滤模式**。
|
||||
|
||||
我举个商城会员系统的例子来解释吧。
|
||||
|
||||
在商城会员系统中,系统通常会根据用户的消费次数、用户消费金额还有用户的注册时间,将用户划分成不同的等级。
|
||||
|
||||
假设现在商城有五星会员(Five-stars Membership)、金牌会员(Golden Membership)和钻石会员(Diamond Membership)。
|
||||
|
||||
而系统现在打算通过邮件,只针对身份是钻石会员的用户发出钻石会员活动邀请。这个时候,我们就可以通过**过滤模式**,将钻石会员的用户从所有用户中筛选出来,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/47/0f/47498fc9b2d41c59ffb286d84c4f220f.jpg" alt="">
|
||||
|
||||
在这个工作流系统中,一个数据处理模块会将输入的数据集过滤成符合条件的数据,然后传输到下一个数据处理模块进行单独处理。
|
||||
|
||||
## 分离模式(Splitter Pattern)
|
||||
|
||||
如果你在处理数据集时并不想丢弃里面的任何数据,而是想把数据分类为不同的类别来进行处理时,你就需要用到分离模式来处理数据。它的工作流系统图通常如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f2/93/f2e872adf258737f35a9121cf89fad93.jpg" alt="">
|
||||
|
||||
需要注意的是,分离模式并不会过滤任何数据,只是将原来的数据集分组了。
|
||||
|
||||
还是以刚刚商城会员系统为例。假设现在商城有五星会员、金牌会员和钻石会员。
|
||||
|
||||
系统现在打算通过邮件,针对**全部的会员**用户发出与他们身份相符的**不同活动**的邀请。
|
||||
|
||||
这个时候,我们就可以通过**分离模式**将用户按照会员等级分组,然后发送相应的活动内容,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c5/85/c5d84c2aab2e02cc6e1d2e9f7c40e185.jpg" alt="">
|
||||
|
||||
需要注意的是,在分离模式下,同样的数据其实是可以被划分到不同的数据处理模块的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8b/15/8b8061a5947b7d7e32fe832290e1ab15.jpg" alt="">
|
||||
|
||||
数据B是可以同时划分到工作流1和工作流2中。其实这种情况挺常见的,我可以给你举个例子来解释。
|
||||
|
||||
在银行系统上,用户可以通过勾选以短信通知或者以邮件通知的方式来提醒用户一笔交易成功。如果用户同时勾选了短信和邮件两种方式,那么属于这个用户的交易信息既会通过短信通知的数据处理模块来处理,也会通过邮件通知数据处理模块来处理。
|
||||
|
||||
## 合并模式(Joiner Pattern)
|
||||
|
||||
合并模式会将多个不同的数据集转换集中到一起,成为一个总数据集,然后将这个总的数据集放在一个工作流中进行处理。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a4/4e/a4827ed21e8af58d30371e8ecf1e744e.jpg" alt="">
|
||||
|
||||
还是以根据活跃在街头的美团外卖电动车的数量来预测美团的股价这个例子来说吧。
|
||||
|
||||
数据接入这一处理模块里,我们的输入数据有自己团队在街道上拍摄到的美团外卖电动车图片和第三方公司提供的美团外卖电动车图片。
|
||||
|
||||
如果我们打算先整合所有数据,然后进行其它数据处理的话,工作流系统图通常如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1c/ed/1c4bc9aaebc908633da174ba847999ed.jpg" alt="">
|
||||
|
||||
## 小结
|
||||
|
||||
今天我们一起学习了在大规模数据处理中用到的四种设计模式,分别是复制模式、过滤模式、分离模式和合并模式。
|
||||
|
||||
在设计大规模数据处理系统的时候,我们都希望能事先设计好一个工作流系统图出来作为参考。
|
||||
|
||||
有了这样一个大规模数据处理的整体蓝图之后,对于我们理解不同的处理模块是如何相互关联或者对未来优化系统设计是有很大帮助的。
|
||||
|
||||
## 思考题
|
||||
|
||||
在一个航空预定系统中,我们需要处理用户注册、购买机票和出行前24小时的提醒等功能。在这整个过程中,系统的数据处理运用了哪几个设计模式呢?
|
||||
|
||||
欢迎你把文章分享给你的朋友。
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<audio id="audio" title="08 | 发布/订阅模式:流处理架构中的瑞士军刀" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d8/4c/d87f9c93756882b87b23223e4a76904c.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
今天我想要与你分享的是在处理大规模数据中十分流行的一种设计模式:发布/订阅模式(Publish/Subscribe Pattern),有些地方也称它为Pub/Sub。
|
||||
|
||||
在了解发布/订阅模式之前,我想先简单介绍几个基础概念——消息(Message)和消息队列(Message Queue)。
|
||||
|
||||
## 消息
|
||||
|
||||
消息是什么呢?
|
||||
|
||||
在分布式架构里,架构中的各个组件(Component)需要相互联系沟通。组件可以是后台的数据库,可以是前端的浏览器,也可以是公司内部不同的服务终端(Service Endpoint)。
|
||||
|
||||
而各个组件间就是依靠通过发送消息互相通讯的。如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/36/90/360c700398719d1abb62e202ab9dd390.jpg" alt="">
|
||||
|
||||
消息可以是任意格式的。例如,我们可以利用JSON格式来传输一个消息,也能利用XML格式来传输一个消息,甚至可以使用一种自己定义的格式。
|
||||
|
||||
## 消息队列
|
||||
|
||||
知道了消息的含义后,你知道消息队列有什么作用吗?
|
||||
|
||||
消息队列在发布/订阅模式中起的是一个持久化缓冲(Durable Buffer)的作用。
|
||||
|
||||
消息的发送方可以发送任意消息至这个消息队列中,消息队列在接收到消息之后会将消息保存好,直到消息的接收方确认已经从这个队列拿到了这个消息,才会将这条消息从消息队列中删除。
|
||||
|
||||
有的消息系统平台如Apache Kafka,它能够让用户自己定义消息队列对消息的保留时间,我将会在介绍Apache Kafka的时候讲到。
|
||||
|
||||
有了消息队列后,整个发送消息的流程就变成下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1e/2e/1ee9fe4ed0fcc75710e718784e03cd2e.jpg" alt="">
|
||||
|
||||
## 发布/订阅模式
|
||||
|
||||
在了解了消息和消息队列后,现在我想和你正式地介绍发布/订阅模式的概念。
|
||||
|
||||
发布/订阅模式指的是消息的发送方可以将消息异步地发送给一个系统中不同组件,而无需知道接收方是谁。在发布/订阅模式中,发送方被称为发布者(Publisher),接收方则被称作订阅者(Subscriber)。
|
||||
|
||||
发布者将消息发送到消息队列中,订阅者可以从消息队列里取出自己感兴趣的消息。
|
||||
|
||||
在发布/订阅模式里,可以有任意多个发布者发送消息,也可以有任意多个订阅者接收消息,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d7/a4/d7a10c4975ad59e11c05a357f0e7f5a4.jpg" alt="">
|
||||
|
||||
读到这里,你可能会有一个疑问,从概念上看,发布/订阅模式只是简单地在消息发送方和消息接收方中间多加了一个消息队列,为什么这种设计架构在实际应用会如此流行呢?我来给你举个例子说明一下。
|
||||
|
||||
现在假设,你是一个移动支付App公司里支付团队的开发人员,App里所有的支付操作都是由你的团队来开发的。而公司最近新成立了一个欺诈预防团队,他们希望从你的支付团队里获取交易数据。
|
||||
|
||||
也就是说,每次有交易发生的时候,你都需要通知他们交易的金额、地点、时间这些数据,让他们能够实时分析这次的交易是否存在欺诈行为。
|
||||
|
||||
按照传统的做法,两个团队需要开会交流,以确定数据消息传输的API和传输数据的格式。确定后,两个团队系统的交流方式会如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e6/29/e6624bd049e75e05b8261d6eda77cb29.jpg" alt="">
|
||||
|
||||
欺诈预防团队将自己需要的数据格式定义在API中告诉支付团队,每次有交易产生的时候,支付系统就会通过调用欺诈预防系统API的方式通知他们。
|
||||
|
||||
一段时间过后,公司希望和商家一起合作推动一项优惠活动,不同的商家会有不同的优惠。公司希望能够精准投放优惠活动的广告给感兴趣的用户,所以又成立了一个新部门,我们叫它广告推荐组吧。
|
||||
|
||||
广告推荐组的同事也希望从你的支付团队里获取交易数据。这个时候,你有两种选择,一种是选择第六讲中说到的批处理方式,另一种就是今天讲的发布/订阅模式。
|
||||
|
||||
批处理方式会从数据库中一次性读取全部用户的交易数据来进行推荐分析。
|
||||
|
||||
这种做法有几个不好的地方。
|
||||
|
||||
它需要你开放自己数据库的权限给广告推荐组,推荐组每次大量读取数据时,可能也会造成你自己的数据库性能下降。同时,还要考虑广告推荐组也想维护一份自己的数据库的需求。
|
||||
|
||||
如果还是按照之前欺诈预防团队的做法,让广告推荐组分享API给你,每次有交易产生的时候再通知他们的话,系统的运行模式会如文中图片所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/5d/39/5de2522f2f436141dbf802ff2a19a439.jpg" alt="">
|
||||
|
||||
看到这里你应该明白了。每一次有一个新的系统想从支付团队里读取数据的话,都要双方开会讨论,定义一个新的API,然后修改支付团队现有的系统,将API加入系统中。
|
||||
|
||||
而且这些API通常都是同步调用的,过多的API调用会让系统的延迟越来越大。这样的设计模式被称作观察者模式(Observer Pattern),系统中的各个组件紧耦合(Tightly Coupled)在一起。
|
||||
|
||||
如果是采用发布/订阅模式来重新设计呢?整个系统就如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f2/00/f2f3daa13f6db54f96c1c18f61a93200.jpg" alt="">
|
||||
|
||||
采用这样的数据处理模式,作为消息发布者的支付团队无需过多考虑以后有多少其它的团队需要读取交易数据,只需要设计好自己提供的数据内容与格式,在每次交易发生时发送消息进消息队列中就可以了。任何对这些数据感兴趣的团队只需要从消息队列中自行读取便可。
|
||||
|
||||
## 发布/订阅模式的优缺点
|
||||
|
||||
说到这里,我们可以看到发布/订阅模式会有以下几个优点:
|
||||
|
||||
<li>
|
||||
松耦合(Loose Coupling):消息的发布者和消息的订阅者在开发的时候完全不需要事先知道对方的存在,可以独立地进行开发。
|
||||
</li>
|
||||
<li>
|
||||
高伸缩性(High Scalability):发布/订阅模式中的消息队列可以独立的作为一个数据存储中心存在。在分布式环境中,更是消息队列更是可以扩展至上千个服务器中。我们从Linkedin公司的技术博客中可以得知,光在2016年,Linkedin公司就维护开发了将近1400个消息队列。
|
||||
</li>
|
||||
<li>
|
||||
系统组件间通信更加简洁:因为不需要为每一个消息的订阅者准备专门的消息格式,只要知道了消息队列中保存消息的格式,发布者就可以按照这个格式发送消息,订阅者也只需要按照这个格式接收消息。
|
||||
</li>
|
||||
|
||||
虽然发布/订阅模式的数据处理模式优点多多,但是还是存在着自身的缺点的。
|
||||
|
||||
例如,在整个数据模式中,我们不能保证发布者发送的数据一定会送达订阅者。如果要保证数据一定送达的话,需要开发者自己实现响应机制。
|
||||
|
||||
在硅谷,很多大型云平台都是运用这个发布/订阅数据处理模式。
|
||||
|
||||
例如,Google的Cloud Pub/Sub平台,AWS的Amazon Simple Notification Service(SNS)。被Linkedin、Uber等硅谷大厂所广泛使用的开源平台Apache Kafka也是搭建在发布/订阅数据处理模式之上的。甚至是连Redis也支持原生的发布/订阅模式。
|
||||
|
||||
Apache Kafka作为一个被在硅谷大厂与独角兽广泛使用的开源平台,如果你是在创业阶段的话,说不定可以用得上,所以在这里我想简单介绍一下Apache Kafka。
|
||||
|
||||
在Apache Kafka中,消息的发送方被称为Producer,消息的接收方被称为Consumer,而消息队列被称为Topic。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b3/0e/b31f636250ed7e4ea9d20ef6bac3e90e.jpg" alt="">
|
||||
|
||||
Apache Kafka在判断消息是否被接收方接收是利用了Log offset机制。
|
||||
|
||||
什么是Log offset机制呢?我举个例子给你解释一下。
|
||||
|
||||
假设发送方连续发送了5条数据到消息队列Topics中,这5条消息被编号为10000、10001、10002、10003和10004。
|
||||
|
||||
如果接收方读取数据之后回应消息队列它接收的Log offset是10000、10001和10003,那么消息队列就会认为接收方最多只接收了消息10000和10001,剩下的消息10002、10003和10004则会继续发送给接收方,直到接收方回应接收了消息10002、10003和10004。
|
||||
|
||||
## 发布/订阅模式的适用场景
|
||||
|
||||
我们说回到发布/订阅模式来,看看它能用在哪些场景。
|
||||
|
||||
如果你在处理数据的时候碰到以下场景,那么就可以考虑使用发布/订阅的数据处理模式。
|
||||
|
||||
<li>
|
||||
系统的发送方需要向大量的接收方广播消息。
|
||||
</li>
|
||||
<li>
|
||||
系统中某一个组件需要与多个独立开发的组件或服务进行通信,而这些独立开发的组件或服务可以使用不同的编程语言和通信协议。
|
||||
</li>
|
||||
<li>
|
||||
系统的发送方在向接收方发送消息之后无需接收方进行实时响应。
|
||||
</li>
|
||||
<li>
|
||||
系统中对数据一致性的要求只需要支持数据的最终一致性(Eventual Consistency)模型。
|
||||
</li>
|
||||
|
||||
要提醒你注意的一点是,如果系统的发送方在向接收方发送消息之后,需要接收方进行实时响应的话,那么绝大多数情况下,都不要考虑使用发布/订阅的数据处理模式。
|
||||
|
||||
## 小结
|
||||
|
||||
今天我们一起学习了大规模数据处理中一种十分流行的设计模式——发布/订阅模式。它能够很好地解耦(Decouple)系统中不同的组件,许多实时的流处理架构就是利用这个数据处理的设计模式搭建起来的。因为发布/订阅模式同时具有很好的伸缩性。
|
||||
|
||||
如果你在开发的场景适合我所讲到的适应场景,可以优先考虑使用发布/订阅模式。
|
||||
|
||||
## 思考题
|
||||
|
||||
你认为微信的朋友圈功能适合使用发布/订阅模式吗?为什么?
|
||||
|
||||
欢迎你把答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
<audio id="audio" title="09 | CAP定理:三选二,架构师必须学会的取舍" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/fd/01/fd060d7040dabdb22e0a0ba03755ed01.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
今天我要与你分享的主题是CAP定理。
|
||||
|
||||
在分布式系统的两讲中,我们一起学习到了两个重要的概念:可用性和一致性。
|
||||
|
||||
而今天,我想和你讲解一个与这两个概念相关,并且在设计分布式系统架构时都会讨论到的一个定理——**CAP定理**(CAP Theorem)。
|
||||
|
||||
## CAP定理
|
||||
|
||||
CAP这个概念最初是由埃里克·布鲁尔博士(Dr. Eric Brewer)在2000年的ACM年度学术研讨会上提出的。
|
||||
|
||||
如果你对这次演讲感兴趣的话,可以翻阅他那次名为“[Towards Robust Distributed Systems](https://people.eecs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf)”的演讲deck。
|
||||
|
||||
在两年之后,塞思·吉尔伯特(Seth Gilbert)和麻省理工学院的南希·林奇教授(Nancy Ann Lynch)在他们的论文“Brewer’s conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services”中证明了这一概念。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2b/8f/2bfd96a97ce8d38834105964d0cb0e8f.png" alt="">
|
||||
|
||||
他们在这篇论文中证明了:在任意的分布式系统中,一致性(Consistency),可用性(Availability)和分区容错性(Partition-tolerance)这三种属性最多只能同时存在两个属性。
|
||||
|
||||
下面,我来为你解读一下这三种属性在这篇论文里的具体意思。
|
||||
|
||||
## C属性:一致性
|
||||
|
||||
一致性在这里指的是**线性一致性(Linearizability Consistency)**。在线性一致性的保证下,所有分布式环境下的操作都像是在单机上完成的一样,也就是说图中Sever A、B、C的状态一直是一致的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/17/41/17a4df9ff551c932bf60ca459fe3b641.jpg" alt="">
|
||||
|
||||
打个比方,现在有两个操作(Operation),操作A和操作B,都需要在同一个分布式系统上完成。
|
||||
|
||||
我们假设操作A作用在系统上的时候,所看见的所有系统状态(State)叫作状态A。而操作B作用在系统上的时候,所看见的所有系统状态叫作状态B。
|
||||
|
||||
如果操作A是在操作B之前发生的,并且操作A成功了。那么系统状态B必须要比系统状态A更加新。
|
||||
|
||||
可能光看理论的话你还是会觉得这个概念有点模糊,那下面我就以一个具体例子来说明吧。
|
||||
|
||||
假设我们设计了一个分布式的购物系统,在这个系统中,商品的存货状态分别保存在服务器A和服务器B中。我们把存货状态定义为“有货状态”或者“无货状态”。在最开始的时候,服务器A和服务器B都会显示商品为有货状态。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ed/38/ed38011a91fdd19021b6450415f5a738.jpg" alt="">
|
||||
|
||||
等一段时间过后,商品卖完了,后台就必须将这两台服务器上的商品状态更新为无货状态。
|
||||
|
||||
因为是在分布式的环境下,商品状态的更新在服务器A上完成了,显示为无货状态。而服务器B的状态因为网络延迟的原因更新还未完成,还是显示着有货状态。
|
||||
|
||||
这时,恰好有两个用户使用着这个购物系统,先后发送了一个查询操作(Query Operation)到后台服务器中查询商品状态。
|
||||
|
||||
我们假设是用户A先查询的,这个查询操作A被发送到了服务器A上面,并且成功返回了商品是无货状态的。用户B在随后也对同一商品进行查询,而这个查询操作B被发送到了服务器B上面,并且成功返回了商品是有货状态的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c3/08/c3421ee2650ba291bbf630448d3f5f08.jpg" alt="">
|
||||
|
||||
我们知道,对于整个系统来说,商品的系统状态应该为无货状态。而操作A又是在操作B之前发送并且成功完成的,所以如果这个系统有线性一致性这个属性的话,操作B所看到的系统状态理论上应该是无货状态。
|
||||
|
||||
但在我们这个例子中,操作B却返回了有货状态。所以我们说,这个分布式的购物系统并不满足论文里所讲到的线性一致性。
|
||||
|
||||
聊完了一致性,我们一起来看看可用性的含义。
|
||||
|
||||
## A属性:可用性
|
||||
|
||||
可用性的概念比较简单,在这里指的是**在分布式系统中,任意非故障的服务器都必须对客户的请求产生响应**。
|
||||
|
||||
当系统满足可用性的时候,不管出现什么状况(除非所有的服务器全部崩溃),都能返回消息。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a2/80/a22d6b3032f045565b952076f5f1ce80.jpg" alt="">
|
||||
|
||||
也就是说,当客户端向系统发送请求,只要系统背后的服务器有一台还未崩溃,那么这个未崩溃的服务器必须最终响应客户端。
|
||||
|
||||
## P属性:分区容错性
|
||||
|
||||
在了解了可用性之后,你还需要了解分区容错性。它分为两个部分,“分区”和“容错”。
|
||||
|
||||
在一个分布式系统里,如果出现一些故障,可能会使得部分节点之间无法连通。由于这些故障节点无法联通,造成整个网络就会被分成几块区域,从而使数据分散在这些无法连通的区域中的情况,你可以认为这就是发生了分区错误。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/59/d4/59b52bb37de477286a70c355fe0fe1d4.jpg" alt="">
|
||||
|
||||
如图所示,如果你要的数据只在Sever A中保存,当系统出现分区错误,在不能直接连接Sever A时,你是无法获取数据的。我们要“分区容错”,意思是即使出现这样的“错误”,系统也需要能“容忍”。也就是说,就算错误出现,系统也必须能够返回消息。
|
||||
|
||||
分区容错性,在这里指的是我们的**系统允许网络丢失从一个节点发送到另一个节点的任意多条消息**。
|
||||
|
||||
我们知道,在现代网络通信中,节点出现故障或者网络出现丢包这样的情况是时常会发生的。
|
||||
|
||||
如果没有了分区容错性,也就是说系统不允许这些节点间的通讯出现任何错误的话,那我们日常所用到的很多系统就不能再继续工作了。
|
||||
|
||||
所以在**大部分情况下,系统设计都会保留P属性,而在C和A中二选一**。
|
||||
|
||||
论文中论证了在任意系统中,我们最多可以保留CAP属性中的两种,也就是CP或者AP或者CA。关于具体的论证过程,如果你感兴趣的话,可以自行翻阅论文查看。
|
||||
|
||||
你可能会问,在我们平常所用到的开发架构中,有哪些系统是属于CP系统,有哪些是AP系统又有哪些是CA系统呢?我来给你介绍一下:
|
||||
|
||||
- CP系统:Google BigTable, Hbase, MongoDB, Redis, MemCacheDB,这些存储架构都是放弃了高可用性(High Availablity)而选择CP属性的。
|
||||
- AP系统:Amazon Dynamo系统以及它的衍生存储系统Apache Cassandra和Voldemort都是属于AP系统
|
||||
- CA系统:Apache Kafka是一个比较典型的CA系统。
|
||||
|
||||
我在上面说过,P属性在现代网络时代中基本上是属于一个必选项,那为什么Apache Kafka会放弃P选择CA属性呢?我来给你解释一下它的架构思想。
|
||||
|
||||
## 放弃了P属性的Kafka Replication
|
||||
|
||||
在Kafka发布了0.8版本之后,Kafka系统引入了Replication的概念。Kafka Relocation通过将数据复制到不同的节点上,从而增强了数据在系统中的持久性(Durability)和可用性(Availability)。在Kafka Replication的系统设计中,所有的数据日志存储是设计在同一个数据中心(Data Center)里面的,也就是说,在同一个数据中心里网络分区出现的可能性是十分之小的。
|
||||
|
||||
它的具体架构是这样的,在Kafka数据副本(Data Replication)的设计中,先通过Zookeeper选举出一个领导者节点(Leader)。这个领导者节点负责维护一组被称作同步数据副本(In-sync-replica)的节点,所有的数据写入都必须在这个领导者节点中记录。
|
||||
|
||||
我来举个例子,假设现在数据中心有三台服务器,一台被选为作为领导者节点,另外两台服务器用来保存数据副本,分别是Replication1和Replication2,它们两个节点就是被领导者节点维护的同步数据副本了。领导者节点知道它维护着两个同步数据副本。
|
||||
|
||||
如果用户想写入一个数据,假设是“Geekbang”
|
||||
|
||||
<li>
|
||||
用户会发请求到领导者节点中想写入“Geekbang”。
|
||||
</li>
|
||||
<li>
|
||||
领导者节点收到请求后先在本地保存好,然后也同时发消息通知Replication1和Replication2。
|
||||
</li>
|
||||
<li>
|
||||
Replication1和Replication2收到消息后也保存好这条消息并且回复领导者节点写入成功。
|
||||
</li>
|
||||
<li>
|
||||
领导者节点记录副本1和副本2都是健康(Healthy)的,并且回复用户写入成功。
|
||||
</li>
|
||||
|
||||
红色的部分是领导者节点本地日志,记录着有哪些同步数据副本是健康的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d7/ca/d731b39103542c83c98bbe57aca1ecca.jpg" alt="">
|
||||
|
||||
往后用户如果想查询写入的数据,无论是领导者节点还是两个副本都可以返回正确同步的结果。
|
||||
|
||||
那假如分区出现了该怎么办呢?例如领导者节点和副本1无法通讯了,这个时候流程就变成这样了。
|
||||
|
||||
<li>
|
||||
用户会发请求到领导者节点中想写入“Geekbang”。
|
||||
</li>
|
||||
<li>
|
||||
领导者节点收到请求后先在本地保存好,然后也同时发消息通知Replication1和Replication2。
|
||||
</li>
|
||||
<li>
|
||||
只有Replication2收到消息后也保存好这条消息并且回复领导者节点写入成功。
|
||||
</li>
|
||||
<li>
|
||||
领导者节点记录副本2是健康的,并且回复用户写入成功。
|
||||
</li>
|
||||
|
||||
同样,红色的部分是领导者节点本地日志,记录着有哪些同步数据副本是健康的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9d/f6/9d97ad203e7869019a84363c847b3cf6.jpg" alt="">
|
||||
|
||||
如果所有副本都无法通讯的时候,Apache Kafka允许系统只有一个节点工作,也就是领导者节点。这个时候所有的写入都只保存在领导者节点了。过程如下,
|
||||
|
||||
<li>
|
||||
用户会发请求到领导者节点中想写入“Geekbang”。
|
||||
</li>
|
||||
<li>
|
||||
领导者节点收到请求后先在本地保存好,然后也同时发消息通知Replication1和Replication2。
|
||||
</li>
|
||||
<li>
|
||||
没有任何副本回复领导者节点写入成功,领导者节点记录无副本是健康的,并且回复用户写入成功。
|
||||
</li>
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7a/5c/7a0b62273c39bbf0dbda1ca3513f595c.jpg" alt="">
|
||||
|
||||
当然,在最坏的情况下,连领导者节点也挂了,Zookeeper会重新去寻找健康的服务器节点来当选新的领导者节点。
|
||||
|
||||
## 小结
|
||||
|
||||
通过今天的学习,我们知道在CAP定理中,一致性,可用性和分区容错性这三个属性最多只能选择两种属性保留。CAP定理在经过了差不多20年的讨论与演化之后,大家对这三个属性可能会有着自己的一些定义。
|
||||
|
||||
例如在讨论一致性的时候,有的系统宣称自己是拥有C属性,也就拥有一致性的,但是这个一致性并不是论文里所讨论到的线性一致性。
|
||||
|
||||
在我看来,作为大规模数据处理的架构师,我们应该熟知自己的系统到底应该保留CAP中的哪两项属性,同时也需要熟知,自己所应用到的平台架构是保留着哪两项属性。
|
||||
|
||||
## 思考题
|
||||
|
||||
如果让你重新设计微博系统中的发微博功能,你会选择CAP的哪两个属性呢?为什么呢?
|
||||
|
||||
欢迎你把答案写在留言区,与我和其他同学一起讨论。
|
||||
|
||||
如果你觉得有所收获,也欢迎把文章分享给你的朋友。
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<audio id="audio" title="10 | Lambda架构:Twitter亿级实时数据分析架构背后的倚天剑" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/71/1b/717466ee77f75802355aa8cc468a201b.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
今天我要与你分享的主题是Lambda架构。
|
||||
|
||||
通过这一讲,你可以了解什么是Lambda架构,以及它为什么能够成为Twitter亿级实时数据分析架构背后的“倚天剑”。
|
||||
|
||||
在学习了架构师的必备技能后,你是否已经摩拳擦掌,跃跃欲试地想要上手一个实际项目了呢?没问题,我们一起来看一个我的架构经历里的真实项目。
|
||||
|
||||
情况是这样的,我们正运行着广告精准投放业务,并且拥有海量的用户网站访问行为。我们需要进行用户行为分析来建立一个模型,然后根据这个模型来投放用户喜好的广告。
|
||||
|
||||
你可能想到了批处理架构。没错,这个时候数据批处理架构无疑是一种很好的选择。
|
||||
|
||||
可是我们不要忘了,之前讲过批处理架构有着高延时性的不足,而互联网用户行为的数据往往可以达到Pb或Eb,甚至是Zb的级别。做这种分析挖掘用户行为的任务,往往能耗时好几个小时甚至是几天。这样的话,我们根据模型精准投放给特定用户的广告就会有一定延时了。
|
||||
|
||||
那我们只用流处理架构行不行呢?
|
||||
|
||||
在广告精准投放的业务需求下,只用流处理架构会造成忽略了用户的历史网站访问行为,一些异常行为可能会让我们的服务投放错误的广告。
|
||||
|
||||
例如,用户A的电脑暂时借给用户B使用了一下,而用户B浏览了一些新的网站类型(与用户A不同)。这种情况下,我们无法判断用户A实际上是否对这类型的广告感兴趣,所以不能根据这些新的浏览记录给用户A推送广告。
|
||||
|
||||
这个时候应该怎么优化我们的架构呢?我们先把问题放一放,在介绍完了Lambda架构之后或许会对你有所启发。
|
||||
|
||||
## Lambda架构
|
||||
|
||||
Lambda架构(Lambda Architecture)是由Twitter工程师南森·马茨(Nathan Marz)提出的大数据处理架构。这一架构的提出基于马茨在BackType和Twitter上的分布式数据处理系统的经验。
|
||||
|
||||
Lambda架构使开发人员能够构建大规模分布式数据处理系统。它具有很好的灵活性和可扩展性,也对硬件故障和人为失误有很好的容错性。
|
||||
|
||||
Lambda架构总共由三层系统组成:**批处理层**(Batch Layer),**速度处理层**(Speed Layer),以及用于响应查询的**服务层**(Serving Layer)。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8f/23/8fe667211309978b2dd6cb6948939923.jpg" alt="">
|
||||
|
||||
在Lambda架构中,每层都有自己所肩负的任务。
|
||||
|
||||
批处理层存储管理主数据集(不可变的数据集)和预先批处理计算好的视图。
|
||||
|
||||
批处理层使用可处理大量数据的分布式处理系统预先计算结果。它通过处理所有的已有历史数据来实现数据的准确性。这意味着它是基于完整的数据集来重新计算的,能够修复任何错误,然后更新现有的数据视图。输出通常存储在只读数据库中,更新则完全取代现有的预先计算好的视图。
|
||||
|
||||
速度处理层会实时处理新来的大数据。
|
||||
|
||||
速度层通过提供最新数据的实时视图来最小化延迟。速度层所生成的数据视图可能不如批处理层最终生成的视图那样准确或完整,但它们几乎在收到数据后立即可用。而当同样的数据在批处理层处理完成后,在速度层的数据就可以被替代掉了。
|
||||
|
||||
本质上,速度层弥补了批处理层所导致的数据视图滞后。比如说,批处理层的每个任务都需要1个小时才能完成,而在这1个小时里,我们是无法获取批处理层中最新任务给出的数据视图的。而速度层因为能够实时处理数据给出结果,就弥补了这1个小时的滞后。
|
||||
|
||||
所有在批处理层和速度层处理完的结果都输出存储在服务层中,服务层通过返回预先计算的数据视图或从速度层处理构建好数据视图来响应查询。
|
||||
|
||||
好了,我们回到刚刚的问题中。我们如何做到既能实时分析用户新的网站浏览行为又能兼顾到用户的网站浏览行为历史呢?没错,就是利用Lambda架构。
|
||||
|
||||
所有的新用户行为数据都可以同时流入批处理层和速度层。批处理层会永久保存数据并且对数据进行预处理,得到我们想要的用户行为模型并写入服务层。而速度层也同时对新用户行为数据进行处理,得到实时的用户行为模型。
|
||||
|
||||
而当“应该对用户投放什么样的广告”作为一个查询(Query)来到时,我们从服务层既查询服务层中保存好的批处理输出模型,也对速度层中处理的实时行为进行查询,这样我们就可以得到一个完整的用户行为历史了。
|
||||
|
||||
一个查询就如下图所示,既通过批处理层兼顾了数据的完整性,也可以通过速度层弥补批处理层的高延时性,让整个查询具有实时性。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/87/b1/8736ead4184d00aa190b4ffbad2f97b1.jpg" alt="">
|
||||
|
||||
Lambda架构在硅谷一线大公司的应用已经十分广泛,我来带你一起看看一些实际的应用场景。
|
||||
|
||||
## Twitter的数据分析案例
|
||||
|
||||
Twitter在欧美十分受欢迎,而Twitter中人们所发Tweet里面的Hashtag也常常能引爆一些热搜词汇,也就是Most Popular Hashtags。下面我来给你讲述一下如何利用Lambda架构来实时分析这些Hashtags。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/60/20/607c18bece2d57048506374048e74220.jpg" alt="">
|
||||
|
||||
在这个实际案例里,我们先用twitter4J的流处理API抓取实时的Twitter推文,同时利用Apache Kafka将抓取到的数据保存并实时推送给批处理层和速度层。
|
||||
|
||||
因为Apache Spark平台中既有批处理架构也兼容了流处理架构,所以我们选择在批处理层和速度层都采用Apache Spark来读取来自Apache Kafka的数据。
|
||||
|
||||
批处理层和速度层在分析处理好数据后会将数据视图输出存储在服务层中,我们将使用Apache Cassandra平台来存储他们的数据视图。Apache Cassandra将批处理层的视图数据和速度层的实时视图数据结合起来,就可以得到一系列有趣的数据。
|
||||
|
||||
例如,我们根据每一条Tweet中元数据(Metadata)里的location field,可以得知发推文的人的所在地。而服务层中的逻辑可以根据这个地址信息进行分组,然后统计在不同地区的人所关心的Hashtag是什么。
|
||||
|
||||
时间长达几周或者的几个月的数据,我们可以结合批处理层和速度层的数据视图来得出,而快至几个小时的数据我们又可以根据速度层的数据视图来获知,怎么样?这个架构是不是十分灵活?
|
||||
|
||||
看到这里,你可能会问,我在上面所讲的例子都是来自些科技巨头公司,如果我在开发中面对的数据场景没有这么巨大,又或者说我的公司还在创业起步阶段,我是否可以用到Lambda架构呢?
|
||||
|
||||
答案是肯定的!我下面将和你一起分享一个在硅谷旧金山创业公司的App后台架构。
|
||||
|
||||
## Smart Parking案例分析
|
||||
|
||||
在硅谷旧金山地区上班生活的小伙伴肯定都知道,找停车位是一大难题。这里地少车多,每次出行,特别是周末,找停车位都要绕个好几十分钟才能找得到。
|
||||
|
||||
智能停车App就是在这样的背景下诞生的。这个App可以根据大规模数据所构建的视图推荐最近的车位给用户。
|
||||
|
||||
看到这里,我想先请你结合之前所讲到的广告精准投放案例,思考一下Lambda架构是如何应用在这个App里的,然后再听我娓娓道来。
|
||||
|
||||
好,我们来梳理一下各种可以利用到的大数据。
|
||||
|
||||
首先是可以拿到各类停车场的数据。这类数据的实时性虽然不一定高,但是数据的准确性高。那我们能不能只通过这类大数据来推荐停车位呢?
|
||||
|
||||
我来给你举个极端的例子。假设在一个区域有三个停车场,停车场A现在只剩下1个停车位了。
|
||||
|
||||
停车场B和C还有非常多的空位。而在这时候距离停车场比A较近的位置有10位车主在使用这个App寻求推荐停车位。如果只通过车主和停车场的距离和停车场剩余停车位来判断的话,App很有可能会将这个只剩下一个停车位的停车场A同时推荐给这10位用户。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/85/69/850cfb80f7da8cfb0656627e902e8469.jpg" alt="">
|
||||
|
||||
结果可想而知,只有一位幸运儿能找到停车位,剩下的9位车主需要重新寻找停车位。
|
||||
|
||||
如果附近又出现了只有一个停车位的停车场呢?同理,这个App又会推荐这个停车场给剩下的9位用户。这时又只能有一位幸运儿找到停车位。
|
||||
|
||||
如此反复循环,用户体验会非常差,甚至会导致用户放弃这个App。
|
||||
|
||||
那我们有没有办法可以改进推荐的准确度呢?
|
||||
|
||||
你可能会想到我们可以利用这些停车场的历史数据,建立一个人工智能的预测模型,在推荐停车位的时候,不单单考虑到附近停车场的剩余停车位和用户与停车场的相邻距离,还能将预测模型应用在推荐里,看看未来的一段时间内这个停车场是否有可能会被停满了。
|
||||
|
||||
这时候我们的停车位推荐系统就变成了一个基于分数(Score)来推荐停车位的系统了。
|
||||
|
||||
好了,这个时候的系统架构是否已经达到最优了呢?你有想到应用Lambda架构吗?
|
||||
|
||||
没错,这些停车场的历史数据或者每隔半小时拿到的停车位数据,我们可以把它作为批处理层的数据。
|
||||
|
||||
那速度层的数据呢?我们可以将所有用户的GPS数据聚集起来,这些需要每秒收集的GPS数据刚好又是速度层所擅长的实时流处理数据。从这些用户的实时GPS数据中,我们可以再建立一套预测模型来预测附近停车场位置的拥挤程度。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/68/31/68c66d013bca9ae36adc71094a82e931.jpg" alt="">
|
||||
|
||||
服务层将从批处理层和速度层得到的分数结合后将得到最高分数的停车场推荐给用户。这样利用了历史数据(停车场数据)和实时数据(用户GPS数据)能大大提升推荐的准确率。
|
||||
|
||||
## 小结
|
||||
|
||||
在了解Lambda架构后,我们知道Lambda架构具有很好的灵活性和可扩展性。我们可以很方便地将现有的开源平台套用入这个架构中,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e8/8b/e874291dc460e8bfdbe8221ce0fe3d8b.jpg" alt="">
|
||||
|
||||
当开发者需要迁移平台时,整体的架构不需要改变,只需要将逻辑迁移到新平台中。
|
||||
|
||||
例如,可以将Apache Spark替换成Apache Storm。而因为我们有批处理层这一概念,又有了很好的容错性。
|
||||
|
||||
假如某天开发者发现逻辑出现了错误,只需要调整算法对永久保存好的数据重新进行处理写入服务层,经过多次迭代后整体的逻辑便可以被纠正过来。
|
||||
|
||||
从我的开发经验来看,现在有很多的开发项目可能已经有了比较成熟的架构或者算法了。
|
||||
|
||||
但是如果我们平时能多思考一下现有架构的瓶颈,又或者想一想现在的架构能不能改善得更好,有了这样的思考,在学习到这些经典优秀架构之后,说不定真的能让现有的架构变得更好。
|
||||
|
||||
也就是说,作为一名优秀的架构师,“现有的架构能不能做得更好?”应该是一个需要经常思考的问题。
|
||||
|
||||
## 思考题
|
||||
|
||||
你所做的项目开发能否利用Lambda架构呢?在生活中有没有哪些大数据处理场景可以利用Lambda架构呢?
|
||||
|
||||
欢迎在评论中留言,与我和其他同学一起讨论。
|
||||
|
||||
如果你觉得今天有些不一样的收获,也欢迎你把这篇文章分享给你的好友。
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<audio id="audio" title="11 | Kappa架构:利用Kafka锻造的屠龙刀" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/89/94/89bac8f39e695ce13b53a1ffda85f594.mp3"></audio>
|
||||
|
||||
你好,我是蔡元楠。
|
||||
|
||||
今天我要分享的主题是Kappa架构。
|
||||
|
||||
同样身为大规模数据处理架构,Kappa架构这把利用Kafka锻造的“屠龙刀”,它与Lambda架构的不同之处在哪里呢?
|
||||
|
||||
上一讲中,我讲述了在处理大规模数据时所用到经典架构,Lambda架构。我先来带你简要回顾一下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8f/23/8fe667211309978b2dd6cb6948939923.jpg" alt="">
|
||||
|
||||
Lambda架构结合了批处理和流处理的架构思想,将进入系统的大规模数据同时送入这两套架构层中,分别是批处理层(Batch Layer)和速度层(Speed Layer),同时产生两套数据结果并存入服务层。
|
||||
|
||||
批处理层有着很好的容错性,同时也因为保存着所有的历史记录,使产生的数据集具有很好的准确性。速度层可以及时地处理流入的数据,因此具有低延迟性。最终服务层将这两套数据结合,并生成一个完整的数据视图提供给用户。
|
||||
|
||||
Lambda架构也具有很好的灵活性,你可以将现有开源生态圈中不同的平台套入这个架构,具体请参照上一讲内容。
|
||||
|
||||
## Lambda架构的不足
|
||||
|
||||
虽然Lambda架构使用起来十分灵活,并且可以适用于很多的应用场景,但在实际应用的时候,Lambda架构也存在着一些不足,主要表现在它的维护很复杂。
|
||||
|
||||
使用Lambda架构时,架构师需要维护两个复杂的分布式系统,并且保证他们逻辑上产生相同的结果输出到服务层中。
|
||||
|
||||
举个例子吧,我们在部署Lambda架构的时候,可以部署Apache Hadoop到批处理层上,同时部署Apache Flink到速度层上。
|
||||
|
||||
我们都知道,在分布式框架中进行编程其实是十分复杂的,尤其是我们还会针对不同的框架进行专门的优化。所以几乎每一个架构师都认同,Lambda架构在实战中维护起来具有一定的复杂性。
|
||||
|
||||
那要怎么解决这个问题呢?我们先来思考一下,造成这个架构维护起来如此复杂的根本原因是什么呢?
|
||||
|
||||
维护Lambda架构的复杂性在于我们要同时维护两套系统架构:批处理层和速度层。我们已经说过了,在架构中加入批处理层是因为从批处理层得到的结果具有高准确性,而加入速度层是因为它在处理大规模数据时具有低延时性。
|
||||
|
||||
那我们能不能改进其中某一层的架构,让它具有另外一层架构的特性呢?
|
||||
|
||||
例如,改进批处理层的系统让它具有更低的延时性,又或者是改进速度层的系统,让它产生的数据视图更具准确性和更加接近历史数据呢?
|
||||
|
||||
另外一种在大规模数据处理中常用的架构——Kappa架构(Kappa Architecture),便是在这样的思考下诞生的。
|
||||
|
||||
## Kappa架构
|
||||
|
||||
Kappa架构是由LinkedIn的前首席工程师杰伊·克雷普斯(Jay Kreps)提出的一种架构思想。克雷普斯是几个著名开源项目(包括Apache Kafka和Apache Samza这样的流处理系统)的作者之一,也是现在Confluent大数据公司的CEO。
|
||||
|
||||
克雷普斯提出了一个改进Lambda架构的观点:
|
||||
|
||||
>
|
||||
我们能不能改进Lambda架构中速度层的系统性能,使得它也可以处理好数据的完整性和准确性问题呢?我们能不能改进Lambda架构中的速度层,使它既能够进行实时数据处理,同时也有能力在业务逻辑更新的情况下重新处理以前处理过的历史数据呢?
|
||||
|
||||
|
||||
他根据自身多年的架构经验发现,我们是可以做到这样的改进的。
|
||||
|
||||
在前面Publish–Subscribe模式那一讲中,我讲到过像Apache Kafka这样的流处理平台是具有永久保存数据日志的功能的。通过平台的这一特性,我们可以重新处理部署于速度层架构中的历史数据。
|
||||
|
||||
下面我就以Apache Kafka为例来讲述整个全新架构的过程。
|
||||
|
||||
第一步,部署Apache Kafka,并设置数据日志的保留期(Retention Period)。这里的保留期指的是你希望能够重新处理的历史数据的时间区间。
|
||||
|
||||
例如,如果你希望重新处理最多一年的历史数据,那就可以把Apache Kafka中的保留期设置为365天。如果你希望能够处理所有的历史数据,那就可以把Apache Kafka中的保留期设置为“永久(Forever)”。
|
||||
|
||||
第二步,如果我们需要改进现有的逻辑算法,那就表示我们需要对历史数据进行重新处理。
|
||||
|
||||
我们需要做的就是重新启动一个Apache Kafka作业实例(Instance)。这个作业实例将重头开始,重新计算保留好的历史数据,并将结果输出到一个新的数据视图中。我们知道Apache Kafka的底层是使用Log Offset来判断现在已经处理到哪个数据块了,所以只需要将Log Offset设置为0,新的作业实例就会重头开始处理历史数据。
|
||||
|
||||
第三步,当这个新的数据视图处理过的数据进度赶上了旧的数据视图时,我们的应用便可以切换到从新的数据视图中读取。
|
||||
|
||||
第四步,停止旧版本的作业实例,并删除旧的数据视图。
|
||||
|
||||
这个架构就如同下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f4/ff/f45975d67c2bf9640e6361c8c23727ff.png" alt="">
|
||||
|
||||
与Lambda架构不同的是,Kappa架构去掉了批处理层这一体系结构,而只保留了速度层。你只需要在业务逻辑改变又或者是代码更改的时候进行数据的重新处理。
|
||||
|
||||
当然了,你也可以在我上面讲到的步骤中做一些优化。
|
||||
|
||||
例如不执行第4步,也就是不删除旧的数据视图。这样的好处是当你发现代码逻辑出错时可以及时回滚(Roll Back)到上一个版本的数据视图中去。又或者是你想在服务层提供A/B测试,保留多个数据视图版本将有助于你进行A/B测试。
|
||||
|
||||
在介绍完Kappa架构的概念后,我想通过一个实战例子,来和你进一步学习Kappa架构是如何应用在现实场景中的。
|
||||
|
||||
## 《纽约时报》内容管理系统架构实例
|
||||
|
||||
《纽约时报》是一个在美国纽约出版,在整个美国乃至全世界都具有相当影响力的日报。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a9/2b/a93e2423d7da056ae325ea639b4daf2b.jpg" alt="">
|
||||
|
||||
《纽约时报》的内容管理系统收集、保存着各种各样来源的文档。这些文档有从第三方收集来的资料,也有自己报社编辑部所撰写的故事。当你访问《纽约时报》网站主页时,甚至能够查到162年前的新闻报道。
|
||||
|
||||
可想而知,要处理这么大规模的内容,并将这些内容提供于在线搜索、订阅的个性化推荐以及前端应用程序等等的服务,是一个非常棘手的任务。
|
||||
|
||||
我们先来看看他们曾经使用过的一个老式系统架构。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ff/f1/ff0050f97b7b2b1be3d66353900a66f1.png" alt="">
|
||||
|
||||
我们可以看到,这种系统架构是一种相当典型的基于API的架构,无论是在系统调度上还是使用场景上都存在着自身的不足。我来给你举一些例子。
|
||||
|
||||
<li>
|
||||
不同的内容API可能由不同的团队开发,从而造成API有不同的语义,也有可能需要不同的参数。
|
||||
</li>
|
||||
<li>
|
||||
调用不同API所得到的内容结果可能有不同的格式,在应用端需要重新进行规范化(Standardization)。
|
||||
</li>
|
||||
<li>
|
||||
如果客户端上会实时推送一些新的热点新闻或者突发新闻(Breaking News),那么在上述基于API的架构中,想要实时获知新闻的话,就需要让客户端不停地做轮询操作(Polling)。轮询操作在这里指的是客户端定期地重复调用系统API来查看是否有新的新闻内容,这无疑增加了系统的复杂性。
|
||||
</li>
|
||||
<li>
|
||||
客户端很难访问以前发布过的内容。即便我们知道这些已发布过的新闻列表需要从哪里获取,进行API调用去检索每个单独的新闻列表还是需要花很长的时间。而过多的API调用又会给服务器产生很大的负荷。
|
||||
</li>
|
||||
|
||||
那现在你再来看看当《纽约时报》采取了Kappa架构之后,新的系统架构是什么样的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/73/61/73877382d7b1bfc01bbcd9951ec7ca61.png" alt="">
|
||||
|
||||
首先,Kappa架构在系统调度这个层面上统一了开发接口。
|
||||
|
||||
你可以看到,中间的Kappa架构系统规范好了输入数据和输出数据的格式之后,任何需要传送到应用端的数据都必须按照这个接口输入给Kappa架构系统。而所有的应用端客户都只需要按照Kappa架构系统定义好的输出格式接收传输过来的数据。这样就解决了API规范化的问题。
|
||||
|
||||
我们再来看看增加了中间一层Kappa架构之后数据传输速度上的变化。
|
||||
|
||||
因为Apache Kafka是可以实时推送消息数据的,这样一来,任何传输进中间Kappa架构的数据都会被实时推送到接收消息的客户端中。这样就避免了在应用层面上做定期轮询,从而减少了延时。而对于重新访问或者处理发布过的新闻内容这一问题,还记得我之前和你讲述过的Kafka特性吗?只需要设置Log Offset为0就可以重新读取所有内容了。
|
||||
|
||||
在讲述完Kappa架构和它的应用实例之后,我想强调一下,Kappa架构也是有着它自身的不足的。
|
||||
|
||||
因为Kappa架构只保留了速度层而缺少批处理层,在速度层上处理大规模数据可能会有数据更新出错的情况发生,这就需要我们花费更多的时间在处理这些错误异常上面。
|
||||
|
||||
还有一点,Kappa架构的批处理和流处理都放在了速度层上,这导致了这种架构是使用同一套代码来处理算法逻辑的。所以Kappa架构并不适用于批处理和流处理代码逻辑不一致的场景。
|
||||
|
||||
## 小结
|
||||
|
||||
在最近两讲中,我们学习到了Lambda架构和Kappa架构这两种大规模数据处理架构,它们都各自有着自身的优缺点。我们需要按照实际情况来权衡利弊,看看我们在业务中到底需要使用到哪种架构。
|
||||
|
||||
如果你所面对的业务逻辑是设计一种稳健的机器学习模型来预测即将发生的事情,那么你应该优先考虑使用Lambda架构,因为它拥有批处理层和速度层来确保更少的错误。
|
||||
|
||||
如果你所面对的业务逻辑是希望实时性比较高,而且客户端又是根据运行时发生的实时事件来做出回应的,那么你就应该优先考虑使用Kappa架构。
|
||||
|
||||
## 思考题
|
||||
|
||||
在学习完Lambda架构和Kappa架构之后,你能说出Kappa架构相对Lambda架构的优势吗?
|
||||
|
||||
欢迎你把答案写在留言区,与我和其他同学一起讨论。
|
||||
|
||||
如果你觉得有所收获,也欢迎把文章分享给你的朋友。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user