作者 | L的存在
来源 | 我是程序员小贱(ID:Lanj1995Q)
说到后端开发,难免会遇到各种所谓高大上的「关键词 」,对于我们应届生小白,难免会觉得比较陌生,因为在学校确实比较少遇见这些所谓高大上的东西,那么今天就带着学习的态度和大家分享这些看似可以装逼可以飞的带逼格的关键词吧。
在学校里的项目中,一个 Web 系统可能咋们一个人就搞定,因为几乎不考虑并发量,性能咋样,所谓「过得去 」足矣,但是为了面试考虑,我们又不得不找点类似秒杀系统作为我们简历的支撑项目(即使已经烂大街)。那么先问你第一个问题,为什么就采用了分布式的方案落地这个项目?
当一个人或者几十个使用你的系统,哎呀我去,请求秒回,效果倍棒,于是乎简历砰砰写上却多么牛X,当面试官就会问你你这项目做了啥,测试过没,并发量如何,性能如何?你就…..
当访问系统的用户越来越多,可是我们的系统资源有限,所以需要更多的 CPU 和内存去处理用户的计算请求,当然也就要求更大的网络带宽去处理数据的传输,也需要更多的磁盘空间存储数据。
资源不够,消耗过度,服务器崩溃,系统也就不干活了,那么在这样的情况怎么处理?
垂直伸缩
纵向生长。通过提升单台服务器的计算处理能力来抵抗更大的请求访问量。比如使用更快频率的CPU,更快的网卡,塞更多的磁盘等。
其实这样的处理方式在电信,银行等企业比较常见,让摩托车变为小汽车,更强大的计算机,处理能力也就越强,但是对于运维而言也就越来越复杂。那真的就这样花钱买设备就完事了?
当然不,单台服务器的计算处理能力是有限的,而且也会严重受到计算机硬件水平的制约。
水平伸缩
一台机器处理不过来,我就用多台廉价的机器合并同时处理,人多力量大嘛,通过多台服务器构成分布式集群从而提升系统的整体处理能力。这里说到了分布式,那我们看看分布式的成长过程
记住一句话:系统的技术架构是需求所驱动
最初的单体系统,只需要部分用户访问:
做系统的原因当然是有需求,有价值,可赚钱。随着使用系统的用户越来越多,这时候关注的人越来越多,单台服务器扛不住了,关注的人觉得响应真慢,没啥意思,就开始吐槽,但是这一吐槽,导致用户更多,毕竟大家都爱吃瓜。
这样下去不得不进行系统的升级,将数据库和应用分离。
这样子,咋们将数据库和应用程序分离后,部署在不同的服务器中,从1台服务器变为多台服务器,处理响应更快,内容也够干,访问的用户呈指数增长,这多台服务器都有点扛不住了,怎么办?
加一个缓存吧,我们不每次从数据库中读取数据,而将应用程序需要的数据暂存在缓冲中。缓存呢,又分为本地缓存和分布式的缓存。分布式缓存,顾名思义,使用多台服务器构成集群,存储更多的数据并提供缓存服务,从而提升缓存的能力。
加了缓存哪些好处?
应用程序不再直接访问数据库,提升访问效率。因为缓存内容在内存中,不用每次连接存放磁盘中的数据库。
系统越来越火,于是考虑将应用服务器也作为集群。
干啥啥不行,缓存第一名。不吹牛,缓存应用在计算机的各个角落。缓存可说是软件技术中的的杀手锏,无论是程序代码使用buffer,还是网络架构中使用缓存,虚拟机也会使用大量的缓存。
其实最初在CPU中也就开始使用缓存。缓存分为两种,一种是通读缓存,一种是旁路缓存
通读缓存
假设当前应用程序获取数据,如果数据存在于通读缓存中就直接返回。如果不存在于通读缓存,那么就访问数据源,同时将数据存放于缓存中。
下次访问就直接从缓存直接获取。比较常见的为CDN和反向代理。
CDN
CDN称为内容分发网络。想象我们京东购物的时候,假设我们在成都,如果买的东西在成都仓库有就直接给我们寄送过来,可能半天就到了,用户体验也非常好,就不用从北京再寄过来。
同样的道理,用户就可以近距离获得自己需要的数据,既提高了响应速度,又节约了网络带宽和服务器资源。
旁路缓存
应用程序需要自己从数据源读取数据,然后将这个数据写入到旁路缓存中。这样,下次应用程序需要数据的时候,就可以通过旁路缓存直接获得数据了。
缓存的好处
因为大部分缓存的数据存储在内存中,相比于硬盘或者从网络中获取效率更高,响应时间更快,性能更好;
通过 CDN 等通读缓存可以降低服务器的负载能力;
因为缓存通常会记录计算结果。如果缓存命中直接返回,否则需要进行大量的运算。所以使用缓存也减少了CPU 的计算小号,加快处理速度。
缓存缺点
我们缓存的数据来自源数据,如果源数据被修改了,俺么缓存数据很肯能也是被修改过的,成为脏数据,所以怎么办?
过期失效
在每次写入缓存数据的时候标记失效时间,读取数据的时候检查数据是否失效,如果失效了就重新从数据源获取数据。
失效通知
应用程序在更新数据源的时候,通知清除缓存中的数据。
是不是数据使用缓存都有意义呢?
非也,通常放入缓存中的数据都是带有热点的数据,比如当日热卖商品,或者热门吃瓜新闻,这样将数据存放在缓存中,会被多次读取,从而缓存的命中率也会比较高。
在前面中,通过缓存实际上很多时候是解决了读的问题,加快了读取数据的能力。因为缓存通常很难保证数据的持久性和一致性,所以我们通常不会将数据直接写入缓存中,而是写入 RDBMAS 等数据中,那如何提升系统的写操作性能呢?
此时假设两个系统分别为A,B,其中A系统依赖B系统,两者通信采用远程调用的方式,此时如果B系统出故障,很可能引起A系统出故障。
从而不得不单独进行升级,怎么办?
使用消息队列的异步架构,也成为事件驱动模型。
异步相对于同步而言,同步通常是当应用程序调用服务的时候,不得不阻塞等待服务期完成,此时CPU空闲比较浪费,直到返回服务结果后才会继续执行。
举个例子,小蓝今天想在系统中加一个发邮件的功能,通过SMTP和远程服务器通信,但是远程服务器有很多邮件需要等待发送呢,当前邮件就可能等待比较长时间才能发送成功,发送成功后反馈与应用程序。
这个过程中,远程服务器发送邮件的时候,应用程序就阻塞,准确的说是执行应用程序的线程阻塞。
这样阻塞带来什么问题“?
不能释放占用的系统资源,导致系统资源不足,影响系统性能
无法快速给用户响应结果
但是在实际情况中,我们发送邮件,并不需要得到发送结果。比如用户注册,发送账号激活邮件,无论邮件是否发送成功都会收到"返回邮件已经发送,请查收邮件确认激活",怎样才能让应用程序不阻塞?
此时就比较清晰了,调用者将消息发送给消息队列直接返回,应用程序收到返回以后继续执行,快读响应用户释放资源。
有专门的消费队列程序从中消息队列取出数据并进行消费。如果远程服务出现故障,只会传递给消费者程序而不会影响到应用程序。
消息队列模型中通常有三个角色,分别为生产者,消息队列和消费者。生产者产生数据封装为消息发送给消息队列,专门的消费程序从消息队列中取出数据,消费数据。
在我看来,消息队列主要是缓冲消息,等待消费者消费。其中消费的方式分为两种:
点对点
对生产者多消费者的情况。一个消息被一个消费者消费
上述的发邮件例子就是典型的点对点模式。互不干扰,其中某个服务出现问题不会印象到全局。
订阅模式
开发人员在消息队列中设置主题,生产者往相应的主题发送数据,消费者从对应的主题中消费数据,每个消费者按照自己业务逻辑分别进行计算
这个比较好理解,比如在用户注册的时候,我们将注册信息放入主题用户中,消费者订阅了这个主题,可能有构造短信消息的消费者,也有推广产品的消费者,都可以根据自己业务逻辑进行数据处理。
使用异步模型的优点
快速响应
不在需要等待。生产者将数据发送消息队列后,可继续往下执行,不虚等待耗时的消费处理
削峰填谷(需要修改)
互联网产品会在不同的场景其并发请求量不同。互联网应用的访问压力随时都在变化,系统的访问高峰和低谷的并发压力可能也有非常大的差距。
如果按照压力最大的情况部署服务器集群,那么服务器在绝大部分时间内都处于闲置状态。
但利用消息队列,我们可以将需要处理的消息放入消息队列,而消费者可以控制消费速度,因此能够降低系统访问高峰时压力,而在访问低谷的时候还可以继续消费消息队列中未处理的消息,保持系统的资源利用率。
降低耦合
如果调用是同步,如果调用是同步的,那么意味着调用者和被调用者必然存在依赖,一方面是代码上的依赖,应用程序需要依赖发送邮件相关的代码,如果需要修改发送邮件的代码,就必须修改应用程序,而且如果要增加新的功能
那么目前主要的消息队列有哪些,其有缺点是什么?(好好记下这个高频题目啦)
一台机器扛不住了,需要多台机器帮忙,既然使用多台机器,就希望不要把压力都给一台机器,所以需要一种或者多种策略分散高并发的计算压力,从而引入负载均衡,那么到底是如何分发到不同的服务器的呢?
最初实现负载均衡采取的方案很直接,直接上硬件,当然也就比较贵,互联网的普及,和各位科学家的无私奉献,各个企业开始部署自己的方案,从而出现负载均衡服务器。
HTTP重定向负载均衡
也属于比较直接,当HTTP请求叨叨负载均衡服务器后,使用一套负载均衡算法计算到后端服务器的地址,然后将新的地址给用户浏览器,浏览器收到重定向响应后发送请求到新的应用服务器从而实现负载均衡,如下图所示:
优点:
简单,如果是java开发工程师,只需要servlet中几句代码即可
缺点:
加大请求的工作量。第一次请求给负载均衡服务器,第二次请求给应用服务器
因为要先计算到应用服务器的 IP 地址,所以 IP 地址可能暴露在公网,既然暴露在了公网还有什么安全可言
DNS负载均衡
了解计算机网络的你应该很清楚如何获取 IP 地址,其中比较常见的就是 DNS 解析获取 IP 地址。
用户通过浏览器发起HTTP请求的时候,DNS 通过对域名进行即系得到 IP 地址,用户委托协议栈的 IP 地址简历 HTTP 连接访问真正的服务器。这样不同的用户进行域名解析将会获取不同的IP地址从而实现负载均衡。
乍一看,和HTTP重定向的方案不是很相似吗而且还有 DNS 解析这一步骤,也会解析出 IP 地址,不一样的暴露?
每次都需要解析吗,当然不,通常本机就会有缓存,在实际的工程项目中通常是怎么样的呢?
通过 DNS 解析获取负载均衡集群某台服务器的地址;
负载均衡服务器再一次获取某台应用服务器,这样子就不会将应用服务器的 IP 地址暴露在官网了。
反向代理负载均衡
这里典型的就是Nginx提供的反向代理和负载均衡功能。用户的请求直接叨叨反向代理服务器,服务器先看本地是缓存过,有直接返回,没有则发送给后台的应用服务器处理。
IP负载均衡
上面一种方案是基于应用层的,IP很明显是从网络层进行负载均衡。TCP./IP协议栈是需要上下层结合的方式达到目标,当请求到达网络层的黑猴。
负载均衡服务器对数据包中的IP地址进行转换,从而发送给应用服务器。
注意,这种方案通常属于内核级别,如果数据比较小还好,但是大部分情况是图片等资源文件,这样负载均衡服务器会出现响应或者请求过大所带来的瓶颈。
数据链路负载均衡
它可以解决因为数据量他打而导致负载均衡服务器带宽不足这个问题。怎么实现的呢。它不修改数据包的IP地址,而是更改mac地址。
应用服务器和负载均衡服务器使用相同的虚拟IP。
以上介绍了几种负载均衡的方式,但是很重要的负载均衡算法却没有设计,其中包含了轮询,随机,最少连接,下面分别对此进行介绍。
公司存在的价值在于流量,流量需要数据,可想而知数据的存储,数据的高可用可说是公司的灵魂。那么改善数据的存储都有哪些手段或方法呢?
数据主从复制
主从复制比较好理解,需要使用两个数据库存储一样的数据。其原理为当应用程序A发送更新命令到主服务器的时候,数据库会将这条命令同步记录到Binlog中。
然后其它线程会从Binlog中读取并通过远程通讯的方式复制到另外服务器。服务器收到这更新日志后加入到自己Relay Log中,然后SQL执行线程从Relay Log中读取次日志并在本地数据库执行一遍,从而实现主从数据库同样的数据。
主从复制可以方便进行读写分离,可以使用一主多从的方式保证高可用,如果从数据库A挂了,可以将读操作迁移到从数据库完成高可用。
但是如果主数据库挂了咋搞,那就Mysql的主主复制。可是不管上面说的那种方式都不是提升它的存储能力,这就需要进行数据库的分片了。
数据库分片
将一张表分成若干片,其中每一片都包含一部分行记录,然后将每一片存储在不同的服务器中,这样就实现一张表存放在多台服务器中,哪都有哪些分片存储的方案?
最开始使用"硬编码"的方式,此方式从字面上可以理解为直接在代码中指定。假定表为用户表,通过ID的奇偶存放在不同的服务器上,如下图:
这种方式的缺点很明显,当需要增加服务器的时候,就需要改动代码,这就不友好了。比较常见的数据库分片算法是通过余数Hash算法,根据主键ID和服务器的数量取模,根据余数确定服务器。
我们使用谷歌浏览器的时候,输入搜索关键字,就会出现搜索到多少条结果,用时多少,它是如何做到在如此短的时间完成这么大数据量的搜索。
先来想第一个问题,全世界这么多网页在哪里?
互联网的存在让你和我隔着屏幕都知道你多帅。当然,每个网页中都会存在很多其他网页的超链接,这样构成了庞大的网络。
对于搜索引擎而言,目标为解析这些网页获取超链接,下载链接内容(过滤),具体一些说。
将URL存放于池子中,从池子中取出URL模拟请求,下载对应的HTML,并存放于服务器上,解析HTML内容时如果有超链接URL,检测是否已经爬取过,如果没有暂存队列,后面再依次爬取。架构图如下:
将获取的所有网页进行编号并得到网页集合。然后通过分词技术获得每一个单词,组成矩阵如下所示:
就这样按照单词-文档的方式组织起来就叫做倒排索引了。
Google通过建立<单词,地址>这样的文档,主要搜索到单词就能定位到文档的地址列表,然后根据列表中文档的编号就展现文档信息从而实现快速的检索。
如果搜索出来结果很多,Google是如何能更精准的将我们需要的信息呈现给我们呢。它很明显有个排序功能,那是如何排序的?
Google使用了一种叫做"PageRank"的算法,通过计算每个网页的权重,并按照权重排序,权重高的自然就在显示在前面,那问题来了,为啥权重高的,排在前面的通常就是用户所需要的呢?这就得了解下pagerank算法了。
在pagerank中,如果网页A包含网页B说明A认可B,即投一票。如下图ABCD四个网页所示,箭头代表超链接的方向,比如A->B代表A网页包含B的超链接
怎么计算的?
ABCD 初始值都为1,然后根据关系计算权重。比如此时B包含了AD两个网页,那么权重1被分为两个1/2分别给A和D,此时A包含BCD,那么此时A页面新的权重为1/2 + 1/3 + 1 = 11/6。
pagerank 值越受推荐,代表用户越想看到。基于每个网页的 pagerank 值对倒排索引中的文档列表排序,靠前者则是用户想看到的文档。
这种是因为超链接,引入权值的方式排序。还有其他诸如对于商品售卖次数排序或者电影点赞或评价分数排序。
还有通过关键字查找,希望找到和搜索词相关,这个时候可能就会采用词频TF进行排序,词频代表所查词和文档的相关程度。
比如我们搜索"Java后端"出现的结果以"后端"的相关技术。
在大部分的应用中都会涉及到搜索引擎技术,技术庞大且复杂,希望各位老铁根据自身情况搜索相关所需学习,校招面试中不出现盲点即可。
技术的引进一定是想解决某个痛点。我不希望在一个系统中,一小点改动就影响到全局,希望各个功能模块拆分清晰,不管是测试还是运维都能节省更多的时间。
那么单体的架构出现了哪些问题?
代码分支管理困难
各个部门分别完成各自的任务,但是最后需要 merge 在一起成为整个系统,merge过程经历的人都知道,问题是真XX多。所以不再是996
新增功能麻烦
随着项目的效益越来越好,用户的需求也更多,招聘的人可能更多,对于新手来说上来是一脸懵逼的,老员工忙的要死,新员工成为了摸鱼专家
耗尽连接
用户的增多,每个应用都得和数据库连接,给数据库的连接造成太大的压力甚至耗尽连接
微服务
“微"-------微微一笑很倾城,微笑,微小。顾名思义,讲一个大的系统,拆分为一个个小的服务,分别对各个小服务进行管理。这样说感觉太不专业了,专业点
大应用拆分为小模块
小模块不属于集群中
通过远程调用的方式依赖各个独立的模块完成业务的处理
这些小的模块就叫做------微服务,整体也就是所谓的微服务架构。
既然拆分成了小服务,这么多小服务怎么协调成为一个问题,甚至都不知道怎么掉这个服务,所以在微服务的整体架构中出现了注册中心,谁需要调用使用提供的接口即可。如下图所示:
从上图我们能知道主要是三个概念:
服务提供者
微服务的具体提供者,其他微服务通过接口调用即可
服务消费者
对应于服务提供者,按照提供者接口编程即可。这么轻松的嘛,当然很多细节。举个例子,注明的dubbo服务框架,服务接口通过Dubbo框架代理机制访问Dubbo客户端,客户端通过服务接口声明去注册中心查看有哪些服务器,并将这服务器列表给客户端。
客户端然后根据负载均衡策略选择其中一个服务器,通过远程调用的方式发送服务调用。那么使用微服务需要注重哪几点?
不要拿工具硬上需求,结合业务也许会更佳!
高可用,意味着一台机器挂了没事,其他机器可以照常工作,用户体验一样倍棒,用户压根就不知道,卧槽,你居然升级了系统,我居然一点感受都没有。那么高可用总有个标准吧,是百分之80就行还是90?
一个系统突然不能访问的原因很多:
硬件故障
数据库宕机
磁盘孙欢
bug
光缆断了
可用性指标
通过多少个9来衡量,比如大宝系统可用性为4个9,意味着是99.99%,说明它的服务保证运行时间只有0.01不可用。
高可用涉及到技术成本和设备成本,不是说高可用值越高越好,而是根据具体工具具体场景而定,这里分享一些高可用策略。
冗余备份
任何一个服务都有备份,就反复我们都会对我们笔记本电脑相关文件进行备份一样,以防万一。即使一台服务器挂了,可以很快的切换以致于让用户不会觉得"这系统怎么这么渣"。
负载均衡
使用多台服务器分担一台服务器的压力,满足高并发的请求。怎么实现的呢,应用程序会有一个心跳检测/健康检查机制,如果发现不妥切换即可。上面提到的数据库主主复制也是高可用的方案之一,技术思想果然是相通的
限流降级
我们的目标不是没有蛀牙,而是希望整个系统不要挂掉。限流是对部分请求进行丢弃处理,保证大部分的用户可以正常的请求完成任务。
降级:
可以屏蔽部分当前看来不是很有用的任务。比如电商系统做秒杀活动的过程中,确认收货功能给予的压力挺大,暂时看来并不是核心任务,而且系统到期也会自动确认收货,所以暂时关闭,将系统的资源留给准备下单,放购物车的太太们
异地多活:
有时候我在想要是地震,火灾等自然灾害发生的时候,很多系统的数据怎么办啊。想多了撒,大些的系统多会在各个地方部署数据中心,采用异地多活的多机房策略。用户可以访问任何数据中心,那问题来了,用户的请求是如何到达不同的机房去的?
域名解析
公司中的数据财产,其重要程度不言而喻。系统的健壮性和安全性是保证系统持久运行的基础。不要因为数据泄露才去关注安全问题。
也许说到安全问题,首先想到的是“用户名密码”泄漏,数据库被脱裤导致数据泄露,hack直接拿到用户的敏感信息,所以我们通常有哪些手段或方法来尽全力的抵抗hack嘞,办法总比问题多嘛。
数据加解密
通过对用户密码,身份证等敏感数据加密是常用方法。加密方法通常分为:单向散列加密,对称加密和非对称加密。
所谓单向散列加密,主要体现在单向二字,意味着对明文加密后是不可以解密,你即使是给明文加密的加密者,也无法通过密文知道其明文是什么,即加密单向,不支持解密。说的这么绝对?这么无敌?
那只是理论上而已,常用的MD5算法就是大象散列加密,我们完全可以通过彩虹表等方式进行破解。怎么破解?
说个简单的道理,我们设置密码的时候,通常会使用生日?手机号?什么love?什么520?,这些组合是有限的,我们就可以建立一个比如生日和密文的映射表,然后通过XX彩虹表就可得到密码明文。
所以,通常的情况,使用单向散列加密的时候需要加一点盐,这样一来,hack拿到密文,不知道盐,无法建立彩虹表,就更难还原明文。
应用场景:通常应用在用户密码加密。其加密和校验过程如下:
我们通过上图来回顾一下一个网站的注册登录模块中的用户部分,用户注册需要输入用户名和密码,我们一般不会将裸露的将密码直接存放在数据库,不然被脱裤直接算裸奔。
所以,用户输入密码,应用服务器获得密码后,调用单向散列加密算法,将加密的密文存放于数据库,用户下一次登录的时候,用户依然会输入密码。
只是到了Web服务器后,Web服务器会对输入的密码再进行一次单向散列加密,然后和数据库中取出来的密文进行对比,如果相同,则用户的验证成功,通常这样的方式可以保证用户密码的安全性,当然如果加一点盐,会增加破解的难度。
对称加密
对称加密是通过一个加密算法和密钥,对一段明文进行加密后得到密文,然后使用相同的密钥和对应的解密算法进行解密得到明文。
举个例子,我们不会将银行卡卡号,有效期等直接存储在数据库,而是会通过先加密,然后存储于数据库。
使用的时候必须对密文进行解密还原出明文。这个时候使用对称加密算法,存储的时候加密算法进行加密,使用的时候解密算法解密。
非对称加密
非对称加密是说使用一个加密算法和一个加密秘钥进行加密得到密文,但是在解密出明文的时候,其加解密密钥和加密密钥不同,通常加密密钥叫做公钥,解密密钥叫做私钥。
其实我们常用的 HTTPS 即是非对称加密的应用场景。用户在客户端进行通讯的时候,对数据使用的加密密钥和加密算法进行加密得到密文,到了服务端以后,使用解密密钥和算法进行解密得到明文。
但是非对称消耗的资源比较多,所以HTTPS不是每次请求响应都采用非对称加密,而是先利用非对称加密,在客户单和服务器之间交换一个对称加密的密钥,然后每次的请求响应再使用对称加密。
综上,使用费对称加密保证对称加密迷药的安全,使用对称加密密钥保证请求响应数据的安全。
HTTP攻击与防护
HTTP明文协议,咋们通过嗅探工具就可以清晰查看会话内容,这也是hack攻击门槛最低的方式。很常见的也就是SQL注入和XSS攻击。
SQL注入是攻击者在提交请求参数的时候,包含了恶意的SQL脚本:
server处理计算后向数据库提交的SQL如下:
Select id from users where username='Mike';
如果是恶意攻击,提交的HTTP请求如下:
http://www.a.com?username=Mike';drop table users;--
此时最终生成的SQL是:
Select id from users where username='Mike';drop table users;--';
查询完就直接给删除表了。怎么防护?
比较常用的解决方案是使用PrepareStaement预编译,先将SQL交给数据库生成执行计划,后面hack不管提交什么字符串也只能交给这个执行计划执行,不会生成新的SQL,也就不会被攻击啦。
XSS攻击
跨站点脚本攻击,攻击者通过构造恶意的浏览器脚本文件,使其在其他用户的浏览器运行进而进行攻击。
假设小A将含有恶意脚本的请求给360服务器,服务器将恶意的脚本存储在本地的数据库,当其他正常用户通过这个服务器浏览信息的时候,服务器就会读取数据库中含有恶意脚本的数据并呈现给用户,在用户正常使用浏览器的时候达到攻击的目的。
防御:
比较常见的是在入口处对危险的请求比如drop table等进行拦截,设置一个Web应用防火墙将危险隔离。
其实在上面提到的分布式中就有涉及大数据相关知识。无外乎是数据量越来越大,我们如何尽可能使用较低的成本存储更多的数据,给公司企业带来更好的利润。上面说过分布式缓存,负载均衡等技术,其共同特点是如何抵抗高并发的压力,而这里的大数据技术主要谈论的是如何满足大规模的计算。
通过对数据的分析,进而发掘海量数据中的价值,这里的数据包含数据库数据,日志信息,用户行为数据等等。那么这么多不同类型的数据,怎么去存储呢?
分布式文件存储 HDFS 架构
如何将数以万计的服务器组成统一的文件存储系统?其中使用Namenode服务器作为控制块,负责元数据的管理(记录文件名,访问权限,数据存储地址等),而真正的文件存储在DataNode中。
Mapreduce
大量的数据存储下来的目的是通过相应的算法进行数据分析,获得通过深度学习/机器学习进行预测,从而获取有效的价值,这么大的文件,我们不可能将HDFS当做普通的文件,从文件中读取数据然后计算,这样子不知道算到何时何地。
大数据处理经典的处理框架即MapReduce,分为Map和Reduce两个阶段,其中一个Map过程是将每个服务器上启动Map进程,计算后输出一个
下面以WordCount统计所有数据中相同的词频数据为例,详细看看Map和Reduce的过程。
在这个例子中,通过对value中的1组成的列表,reduce对这些1进行求和操作从而得到每个单词的词频。代码实现如下:
public class WordCount {
// Mapper四个参数:第一个Object表示输入key的类型;第二个Text表示输入value的类型;第三个Text表示表示输出键的类型;第四个IntWritable表示输出值的类型。map这里的输出是指输出到reduce
public static class doMapper extends Mapper<Object, Text, Text, IntWritable> {
public static final IntWritable one = new IntWritable(1);//这里的IntWritable相当于Int类型
public static Text word = new Text;//Text相当于String类型
// map参数<keyIn key,valueIn value,Context context>,将处理后的数据写入context并传给reduce
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
//StringTokenizer是Java工具包中的一个类,用于将字符串进行拆分
StringTokenizer tokenizer = new StringTokenizer(value.toString, " ");
//返回当前位置到下一个分隔符之间的字符串
word.set(tokenizer.nextToken);
//将word存到容器中,记一个数
context.write(word, one);
}
}
//参数同Map一样,依次表示是输入键类型,输入值类型,输出键类型,输出值类型。这里的输入是来源于map,所以类型要与map的输出类型对应 。
public static class doReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable;
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
//for循环遍历,将得到的values值累加
for (IntWritable value : values) {
sum += value.get;
}
result.set(sum);
context.write(key, result);//将结果保存到context中,最终输出形式为"key" + "result"
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
System.out.println("start");
Job job = Job.getInstance;
job.setJobName("wordCount");
Path in = new Path("hdfs://***
:9000/user/hadoop/input/buyer_favorite1.txt");//设置这个作业输入数据的路径(***部分为自己liunx系统的localhost或者ip地址)Path out = new Path("hdfs://***
:9000/user/hadoop/output/wordCount"); //设置这个作业输出结果的路径FileInputFormat.addInputPath(job, in);
FileOutputFormat.setOutputPath(job, out);
job.setJarByClass(WordCount.class);// 设置运行/处理该作业的类
job.setMapperClass(doMapper.class);//设置实现了Map步的类
job.setReducerClass(doReducer.class);//设置实现了Reduce步的类
job.setOutputKeyClass(Text.class);//设置输出结果key的类型
job.setOutputValueClass(IntWritable.class);//设置输出结果value的类型
////执行作业
System.exit(job.waitForCompletion(true) ? 0 : 1);
System.out.println("end");
}
}
那么这个map和reduce进程是怎么在分布式的集群中启动的呢?
上图比较清晰地阐述的整个过程,再描述一波。MR中主要是两种进程角色,分别为 JobTracke r和 TaskTracker 两种。
JobTracker在集群中只有一个,而 TaskTracker 存在多个,当 JobClient 启动后,往 JobTracker 提交作业,JobTracker查看文件路径决定在哪些服务器启动 Map 进程。
然后发送命令给 TaskTracker,告诉它要准备执行任务了,TaskTracker收到任务后就会启动 TaskRunner 下载任务对应的程序。
map计算完成,TaskTracker对map输出结果 shuffer 操作然后加载 reduce 函数进行后续计算,这就是各个模块协同工作的简单过程。
上述过程还是比较麻烦,我们能不能直接写SQL,然后引擎帮助我们生成mapreduce代码,就反复我们在web开发的时候,不直接写SQL语句,直接交给引擎那么方便,有的,它就是HIVE。
举个例子:
那么使用MR的计算过程完成这条SQL的处理:
Spark是基于内存计算的大数据并行计算框架。基于此说说上面hadoop中组件的缺点:
磁盘IO开销大。每次执行都需要从磁盘读取并且计算完成后还需要将将中间结果存放于磁盘
表达能力有限。大多数计算都需要转换为Map和Reduce两个操作,难以描述复杂的数据处理
spark优点:
编程模型不限于map和reduce,具有更加灵活的编程模型
spark提供内存计算,带来更高的迭代运算效率且封装了良好的机器学习算法
采用了基于图DAG的任务调度机制
Flink
Flink是大数据处理的新规,发展速度之快,这两年也相继出现中文资料。作为流式数据流执行引擎,针对数据流的分布式计算提供数据分布,数据通信以及容错机制等功能。同时Flink也提供了机器学习库,图计算库等。
附一张去年参加会议回答问题中奖的马克杯,嘻嘻。
关于大数据相关知识点可作为扩充点,在面试的过程中经常会有大数问题,除了从算法的角度来阐述,也可以从这些框架中吸取一些经验。
对于之前从事c/c++开发的我,很多时候是Linux的开发。在学校又没怎么接触系统性的项目,更不知道后端技术的博大进深,可能文中涉及的也就一部分,不过希望还在学校的小伙伴可以知道有这些东西,然后通过强大的搜索引擎,给自己个比较明确的方向,也许会少走点弯路,这周的文章就到这了,goodbye!
点分享