开发者学堂课程【高校精品课-上海交通大学-企业级应用体系架构:Arch. Components 1】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/75/detail/15819
Arch. Components 1(一)
内容介绍:
一、企业应用的基本特征
二、一台机器的优缺点
三、拆分后的数据结构
四、建立分布式缓存
五、主、从服务器
六、统一数据访问模块
七、Nosql 数据库
八、异步通信
九、总结
一、企业应用的基本特征
这是在完成 web 大作业的时候看到的应用系统的结构,当时整个系统是只在一台服务器上面运行,应用程序是存在于TomCat 里面,它在访问后台的,比如说 Mysql 数据库系统,另外还有一些其它的文件,比如图片以及一些其它的txt的一些描述文件,它们就都在文件系统里面存储,不管怎么样整个系统是在一台机器上。
二、一台机器的优缺点
一台机器上本质来说,存在一些优点和缺点。以应用程序和数据库的交互为例,因为在同一台机器上,所以它们之间的通信会非常的快。无论是在做数据库的查找还是做读写的写操作,速度都非常的快。但是它有一个潜在的风险,举个例子,这一台机器要想被别人访问到,那么它一定会有公网上的 IP。
这时数据库也在这台机器上,它是3306的端口,应用程序在 tomcat 里面是8080,于是会出现一个风险。别人如果使用机器的 IP 加3306的端口,直接来访问数据库。除非在安装数据库的时候,把数据库的用户名和密码改写过,否则的话用缺损或者默认的值,别人用默认值来撞库,它就有可能会把数据攻破。
可以回想一下当时在安装 Mysql 的时候,有没有改写过用户名和密码,当时用户名都是 root,密码是128。如果是这样的话就说明这是一个很危险的事情,别人在访问数据可以绕开没有用的程序,直接用公网 IP 加端口号加默认值来进行访问,这种问题其实经常会出现。比如说部署在学校的网络公司里面的一个应用,它就是在分析慕课网站的一些学习数据,当时研究生在开发项目的时候就没有去做用户名和密码的修改,就是默认的值,结果库就被人撞开了,所以这是一个很大的问题。
于是在进行演化的时候,如果条件允许,通常应用层如上图所示,以及它红线为界限,左边的内容是在广域网上,右边的内容是在局域网上,现在数据库服务器只有一个内网 IP,比如说192.168.0.1,如果从外网上直接访问数据库是访问不到这台机器的,因为数据库是一个内网的IP,通过这种方式就可以把它隔离开,这是从安全的角度去考虑这种方式更好。
另外从性能考虑确实也应该把它拆开,对于应用服务器来说,它大量的事情是根据用户的请求去生成后面的动态页面,所以它要求 CPU 比较强,它需要进行大量的处理。而数据库服务器是需要从硬盘里面把数据录到内存里,它需要一个比较大的内存,也就是它在机器上会开一个缓存,把用户访问过的数据一直在进行保存,以供后续的用户直接从缓存里面去读,不需要再到硬盘上去操作。
硬盘的 io 和内存的 io 性能差异是数量级的,这样性能就会快,从这两个描述就可以看到,一个是要求CPU强,一个要求内存大,所以两者本身的对服务器的要求不一样。
把它放到一台服务器里面,机器很难满足两个各自的要求,所以会为应用服务器配置 CPU 比较强的机器,对数据库服务器进行硬盘和内存较大的配置。另外还有一些文件,也会专门放到一些文件服务器里面,文件包括大量的图片、视频、音频等等。它上面会有专门的一些软件来做一些优化,比如说点播的都是视频,那么点播的软件就会对整个硬盘做一些优化。所以当系统变大了之后,会发现因为数据量变大,无论是在关系型数据库里或者是 Mysql 数据库里,或者就是像 V6这样原始的文件。它们在存储的时候以及在访问的时候,它会有一些要求,这些要求就导致把所有东西放在一台服务器里就不合适了,所以现在把它拆开去存放。从安全性和压力的两个角度,要求把应用拆开,当企业级应用发展到一定规模达到企业级应用,企业级的意思是它的数据量有一定的规模,用户的压力也有一定的规模。在一定规模之上,才能叫企业级应用。
之前开发的电子书店,实际上它还不算是企业级应用,所以从来没有要求把系统分开。如果系统真正上线了,就会发现一台机器是不够的,就要把它拆分开。
三、拆分后的数据结构
分开之后再来看数据是在专门的文件服务器上存储或者在数据服务器上面进行存储,但是因为是在不同的机器上进行存储,所以在通信链路上,它一定要走能够在不同的机器上通信这种协议。
可选的协议有 Http 协议,也可以有本地跑的,比如说后面会讲的远程方法调用协议,还可以有一些其它协议,但不管怎么说,这些都比本地的调用性能要差。
如果从数据库服务器把关系型的数据或者在文件服务器上把一些图片通过这种开销比较大的协议拿到了的应用服务器里,为什么不把它在本地缓存一下,下次再访问的时候就不需要再到远程的机器上面去拿,所以需要加一个缓存,注意这里涉及到的缓存和刚才说的数据库需要一个很大的缓存是两个概念。数据库的缓存是对于数据库服务器来说,它不需要频繁的去读取它的硬盘,它直接从内存里面拿到数据,但是它要想把数据返回给应用服务器一端,也就是运行在tomcat的应用,还是要通过开销比较大的协议把数据从数据库服务器传回,而在 tomcat 所在的应用服务器直接设置一个缓存,把拿到的数据放到里面,那么以后再读取时就不需要远程操作,直接在本地读取即可。
所以它就比较快。
然后再看本地的缓存,当然要占这台机器的内存,但是一台机器的内存它是要跑应用程序又要设置缓存有可能会不够,于是就会想去拿一些机器它们上面布大量的内存把它们设置一个分布式的缓存,也就是说应用服务器会把已经读取的对象给它放到远程的分布式的缓存里面去,这样以后从内存里面再读数据就会比较快。但是这里面又碰上一个问题,刚才说从数据库里面拿数据是在缓存里面通过比较耗时间的远程协议把它拿回来,这仍然是远程的,也是要通过一样的协议去访问。
四、建立分布式缓存
但是这里面又有一个差异,数据库的态势实际上是不受应用程序的影响,也就是说如果有另外一个应用,它也来访问数据库,那么数据库服务器会根据用户的应用和B应用读取数据的情况,优化使用它的缓存,那也就是说如果B应用访问数据库的频率比较高,而用户的访问频率比较低,那么态势多半可能就给了 B,也就是说用户想要的数据可能就在缓存里面可被B访问的数据给替换掉,也就是说缓存不受控制,但是如果用户自己建一个分布式缓存,那么缓冲完全受用户控制,用户可以就可以把大量的数据缓存在里面,这是一个完全受用户控制的缓存。比如说数据库服务器里面的数据在缓存的时候,整个数据库服务器上有8GB的缓存,经过一段时间运行之后,会发现 B 应用它占了6g,而用户的缓存数据只占了2GB,因为这件事情不受用户控制,所以用户没有办法要求更大的缓存。
但是如果是认为自己管理的缓存,应用服务器控制缓存,可以每台机器上都有8GB 的缓存,那这样三台机器那就会有24G 的缓存,这样就可以把数据库里24G的内容缓存到内存。
所以建立一个分布式缓存的意思是这种分布式缓存完全是受用户的服务器控制,而不是像数据库服务器里面的态势是控制不到的,所以虽然用户从内存里都读到了数据,都要通过远程的方法拿到数据,但是如果在自己管理分布式缓存里面,它的缓存的数量可以变得很大,就不像关系型数据库这边它的态势、大小、里面多少个用户使用,完全都被屏蔽掉。
所以要建一个分布式缓存,极端情况最后就会发现,其实数据库对于应用就像是一个备份,用户的数据只是在里面持久化存储,将来如果数据的应用崩掉,可以重新从数据库里面再次拿到数据,一旦应用启动可以把大量的需要用到的数据全部放到内存里,以后的操作就和数据库的写操作的硬盘无关,这样可以提高整个系统的性能。通过增加分布式的缓存,可以大大的提高数据访问的性能。
然后再看应用服务器现在的压力,企业级应用的一个特点就是它的数据量比较大,它的前端发过来的请求也就是并发的访问数也比较大,当访问数比较大的时候,应用程序这一关需要有比较强的 CPU,但是一台机器它处理的客户端是有限的,当电子书店部署在网上的时候,是很难预料到到底是有多少请求的,也许的生意非常好全世界的人都在用的网站,那么数据库肯定是不够用的,于是就要建立服务器的集群。建立服务器集群请求来了之后,就希望每一个它们的负载都相似,也就是如果有三台服务器每一个都可以承担1/3的用户访问量,而不是其中一个直接承担了50%的访问量,剩下两个就分担剩下的50%也就是各承担 25%,这样就会出现一个机器非常忙,其它机器非常闲的情况。所以希望通过负载均衡器来对服务器的压力做均衡,也就是当请求来的时候,它会来决定这些请求应该转发给这三台服务器当中的哪一台。负载均衡的策略有很多,后面也会谈到。它基本上会保证所有的服务器负载差相似,它们不会完全相等的原因是有一些原因的限制,导致不可能完全相等,至少有一点现在就会想到在,之前谈到客户端在访问服务器的时候会有一个 HTTP SESSION 的对象来维护状态。当对象第一次访问的时候,假设说它的请求被调度到了应用服务器a 上,那它的对象就在应用服务器1上,那么以后它在会话当中的请求就不能调度到应用服务器2或者3上,因为在这两台机器上是找不到 http session 的,所以它会保证所有的机器的负载大体一样,但不是绝对一样。因为有了负载均衡监控器,就可以不断的去添加应用服务器来提高前端应用服务器处理用户业务请求的能力。
前端没问题之后会看到后端又会出现问题,数据库知道数据库在某种程度上来说它是一个临界资源,是一个 critical 资源,对它进行操作,尤其是写操作的时候是要加锁的,一旦加了锁,比如说一张表加了锁,其实其它的应用是没有办法去进行写操作的,总的来说是数据库为了保证数据的一致性,就是大家之前提到的 transaction,transaction 就是要保证数据的一致性、持久性等等,它就会在数据库上加锁,一旦加锁就会发现大家就等着,那就是 server 系统当中出现了瓶颈,这时候前端再强例如有一个集群,但是输出服务器就一台,在这里卡住怎么办?于是数据库服务器也要建集群,但是数据库的服务器的集群和应用服务器的集群是不一样的。应用服务器只要开一个机器,把应用在上面再部署一个实例就可以了,数据库如果想开两个实例,不能让任何的写操作去做负载均衡,或者说有一部分写到上面的集群,有一部分写到下面的集群,这样一做的话,数据库它就不同步了,要保证数据库虽然也是集群,但它的数据一定要同步一致,于是数据库分为主从备份。
五、主、从服务器
当然在当前的图里面看到的组合层是一对一的,从其实也可以有多个。回想一下,之前提到的mongoDB,它的从不但有多个而且有多种,有的可以接管主服务器,有的是不能接管主服务器,但是它可以备份数据,它在主服务器崩溃之后具备被选举权以及选举权。第三种是连选举权也没有,非选举权也没有,它不参与投票,纯粹只是一个备份,但一个前提是服务器如果不仅一个,它们互相之间的数据必须要是一致的。分为主从的原因是,所有的写操作只能在一个地方执行,比如说所有写操作调度到主服务器上,当服务器上把数据写进去之后,它会去同步从服务器,同步可以是实时的,也可以是最终一致的,比如说到每天晚上去刷一下,它们各有各的优点各有各的缺点,实时一致性对系统压力很大,而且这种从一旦要实时一致的话也要进行锁库的。它的压力很大,但是它可以保证不管怎么访问,访问到数据一定是一致的。对于最终一致性,可以在每一天不忙的时候把主服务器的数据是同步到从服务器上。但是缺点就是在上一次同步和这一次同步之间的数据是不一样的。所以有时候可能会有这样的体验,当在访问一个网站的时候,发现网站上有一个计数值在记述有多少人在访问网页,会发现多刷几次能出现的值就会有两个不一样的值,它是最终的一个值。当写操作写入到主服务器同步到从服务器的时候,从服务器的作用有以下几个方面。
一方面是当备份,当主服务器崩溃的时候,它可以接管。另外一方面,所有的读操作全部可以放到从服务器上,因为它只读它不会改变数据库的内容,所以它不会造成数据库的不一致。所以当压力大要去解决问题的时候,方法是一样的,就是增加服务器的数量,但是对于前端的应用服务器和后端的数据库服务器,在集群里面加服务器数量以后加了新的实例后,它们具体的操作方式是不一样。应用服务器是比较简单的部署了一个应用的新的实例,就是通过负载均衡器,去把新的请求一些压力卸载到它的另外一台服务器上。而数据库服务其实一定要做主从备份,将写操作和读操作分离。
当系统变成上图的形态之后,再去看又发现系统变得非常的大,系统里面会出现像视频、音频、图片等等。比如这张图就是像淘宝、天猫等等大型的网页,需要考虑一个问题,比如在上海有一个主节点,那是不是让全世界的人都访问上海这一个节点。
如果这样受网络情况的影响,也许华东地区访问就很快,但是北京、西北、西南或者国外甚至北美一些国家在访问的时候可能性能会非常差。于是就会想到可以在不同的地方各布一些东西,比如说利用内容分发网络,也就是像爱奇艺、优酷,它在全世界会有不同的主办节点,当在访问它的时候,它会根据IP调度到离它最近的地方,就近去拿取上面的内容,比如说图片、视频、音频等等。这样对于用户来说它直观感受就比如输入 www.iqiyi.com,实际上这一个域名对应的后台是一堆 的 IP,而不是单一的 IP,会有一堆的机器。它会根据用户的 IP 去寻找一个最近的节点,这时候需要用到反向代理服务器。
正向的代理先有一个 proxy,后面是比如有同学 A、同学 B、同学 C,三个同学都通过一个代理去进行访问,对服务器来说看到的是从同一个代理来的请求,它并不知道是有三个用户,这是正向代理(如下图)。
反向代理是看到了网站有 ABC 三个节点,前面有一个代理,当用户请求来的时候,它会去解析它的 IP,找一个离它IP最近的代理过去。用户就近可能访问到A或者B或者C上(如下图)。
它的目的是当要异地去部署多个实例的时候,要让用户看不到,一个域名就可以访问离它最近的某一个节点上获取信息,所以需要内容分发网络来帮助实现这样的目的,然后使用反向代理服务器。
可以看到现在在国内有很多专门运营cdn服务器的厂商,比如蓝汛,未来的系统更大了之后,就可以要求在蓝汛上面去跟它购买服务,把系统部署在上面,它会自动的根据访问量在 CDN 上面部署应用。比如用户的节点本来是在上海,但是买了蓝汛的服务,蓝汛的服务会经常分析用户的应用的访问情况,当它发现北京有大量的用户访问的时候,它就主动的会在现在北京的节点上把内容复制过去产生镜像,离北京近的用户就可以访问到北京的节点上面。以上是在应对大量的用户并且用户来自于不同的地域,而且用户的地域分布很广的时候,想到可以做这样的改进。
再接下来会看到文件系统和数据库,还要进一步的去优化。第一点,文件数据系统的数据非常的大,在一台机器上面很难存储下,所以要用分布式系统来储存文件,比如一个文件大小就是10GB,比如一个高清的电影可能还不支持GB,它在存储的时候可以使用 Hadoop,Hadoop 的基本想法是10GB 的文件不是只用N台服务器组成一个分布式文件系统能存下就行,它还要考虑到文件读取的效率,所以不是把10GB 的文件放到了某一台机器上,而是把它切分成了很多的小块,比如1GB 一块,把它分布地存储在10台机器上。假如分布式文件系统是10台机器,把一个大的文件切成10个在上面。当有用户想访问的时候,可以从10台机器上同时去读数据,就相当于硬盘的 io 扩大了10倍,只要网络带宽足够就会很快。
现在主干网千兆网、万兆网都可以,虽然是 bite 不是 byte,但是10GB 如果正在千兆网是很快的过程。所以当带宽不成问题的时候,它的压力就在磁盘的 io 上,如果把一个文件分小了,变成很多小块存储在多个机器,甚至在一个机器里面还有很多硬盘,在不同的硬盘上存储,那读写效率就会大大的提高。
所以分布式文件系统不单是要有一个集群,要提高存储的容量,还要优化的存储的方式,让用户在读取数据的时候速度会变快。
另外数据库这一端也要做分布式,主从备份里面主数据库服务器和从数据库服务器的备份是一样的,从服务器是为了分担读压力,而写操作是在主服务器上面不断的来处理。从服务器一方面是在执行读操作,一方面是在做备份,但本质上数据库和硬盘通过增加服务器的数量并没有扩充数据库能够存储的数据的容量。也就是如果一台机器能存10个 TB 的数据,建立一个储存备份其实还是只能存10个TB 的数据,因为它俩是完全一样的。
但是现在要讨论的问题是一台机器只能存储10TB 的话,100TB 的数据该怎么存。很显然有一个非常大的一张表的话,要把它横向切开,在不同的机器上去存。所以分布式的数据库它要考虑的是如何把一个逻辑上是一个完整的数据,把它切成若干块存储在分布式的一个集群里面。当有单表的时候,处理是比较简单,但是当有两个表而且两张表之间是有外界关联的时候,这时候要去把它切开以后存储在不同的机器上就会比较复杂。主要是因为如果有两张表分别切分为两个部分,那么一张表上面部分的数据只会与另一张表上面的数据进行交互,不会存在交叉交互的情况。这时候才比较容易放到两台机器上,否则效率就会变得非常低。
用分布式的系统把文件数据库存储提高了容量,并且做了存储优化让它访问更快之后,可以看到在硬盘上存储了文件存在数据库,然后在内存里通过分布式的缓存,把内存里的一些状态也进行分布储存,三个部分全部是分布式的。