(对我而言十分枯燥的一节,可做了解,不是必看。)
噔噔噔噔~接下来要做的就是Java从零到企业级电商项目实战——服务端。
以淘宝为例,简单了解一下大型的企业项目的服务端架构:
这个体系不是一开始就形成的,而是在开发的进行过程中,不断演进出来的。
其实大到架构,小到代码,都是不断演进、不断优化而来的。
接下来的课程安排为:
环境搭建---->数据库及接口---->项目初始化---->用户模块---->分类模块---->商品模块---->购物车模块---->收获地址模块---->支付模块---->订单模块---->云服务器发布---->总结与展望。
虽然这个课程的内容是传统的购物网站,这方面的发展已经很成熟了,但是在它的开发过程中涉及到了非常多的各个领域的知识,对于新手来说很适合用它来入门、了解、掌握这些知识。
接下来我们对大型Java项目架构的演进进行解析:
一开始,一个小网站用一台服务器就够了。文件服务器(File)、数据库(Database)、应用(Application)都部署在一台机器上。随着用户越来越多,访问量越来越大,硬盘、CPU内存开始紧张,一台服务器就不能满足它的需求了。
这个时候,我们就对数据服务和应用服务进行分离,给应用服务器配置更好的CPU内存,给数据服务器配置更好更快更大的硬盘。例如下图,用了三台服务器,分离之后可以提高一定的性能和可用性。比如文件服务器挂掉了,我们还是可以操作应用和数据库的。
随着访问的并发越来越高,为了降低接口访问时间,提高服务性能,于是继续演进。我们发现有很多业务数据不需要每次都从数据库中获取,于是我们使用了缓存,如下图所示。因为80%的业务访问都集中在20%的数据上,也就是俗称的二八原则,如果我们能将这部分数据缓存下来,性能一下子就上来了。而缓存又分为两种:本地缓存和远程缓存,其中远程缓存又分为远程的单机缓存和远程的分布式缓存。
随着访问的QPS不断提高,服务器遭遇了瓶颈,虽然我们也可以购买更强大的硬件,但总会有上限,并且到了后期成本是一个指数级的增长,这个时候我们就需要做一个服务器的集群,我们要加一个新的东西,负载均衡调度服务器,如下图所示。
加了这个负载均衡调度器,服务器集群之后,我们可以横向的扩展服务器了,解决了服务器处理能力的一个瓶颈。
这个时候我们又要思考几个问题(要思考的问题好多呀!),负载均衡的策略都有哪些,各有什么优缺点,适合什么场景。blablabla此处略。
如上图所示,加了这个负载均衡服务器之后,应用服务也变成了一个Cluster,也就是说一个集群。在我们使用这个架构之后,有这样一个场景,我们登陆的时候登陆了A服务器,session信息存储到了A服务器上,假设我们使用的负载均衡策略是根据IP进行一个哈希散列,那么登录信息还可以从A服务器上访问到,但是IP哈希不够分散也不够均匀,这就有可能造成某些服务器压力过大,某些服务器又没有压力,所以这个时候机器的网卡的带宽就有可能成为一个瓶颈。
这个时候我们使用轮询或者最小连接负载的均衡策略,这就导致了我们第一次访问A服务器,把session信息存储到A服务器上,第二次有可能访问到B服务器,这个时候存储在A服务器上的session信息在B服务器上是读取不到的。
我们就要考虑session管理的问题。
有一个解决方案是对于同一连接中的数据包,负载均衡会将其进行一个转换后,转发至后端固定的服务器进行处理,并将它复制到另外一台服务器上。如下图所示。
但是这两个应用服务器之间要不断的同步session信息,而且当大量用户在线的时候,我们服务器占用的内存会过多,所以这个方法不适合做大规模集群,适合机器不多的情况 。
还有两个解决方案,但都有不足,略。
第四个方案:
将所有用户的session信息都统一保存到Session Server当中,应用服务器需要的时候找Session Server拿就好。它也有缺点,在目前的架构中,Session Server是一个单点的,我们如何解决这个单点,保证它的可用性呢?可以把Session Server也做成一个集群。这个方式适用于Session数量及Web服务器数量大的情况。同时,我们改成这种架构之后,我们在写应用的时候,也要调整存储session的业务逻辑。
在面试的时候,可能会问,为什么要做单点登录?有哪些解决方案?各有什么优缺点?为什么选择这种解决方案?在选择这种解决方案的时候是如何平衡和取舍的?
这门课程就可以让我们收获到这些,深入理解网站架构在遇到某些指标瓶颈的时候,都有哪些解决方案,它们都有哪些优缺点,业务功能上如何取舍,如何做出选择。这个过程才是最重要的,一步一步成为一个高手~
再回到这幅图:
我们看数据库,当用户达到一定数量时,数据库又成为了一个瓶颈,那我们如何解决呢?
我们使用了数据库的读写分离,Master是主库,Slave是从库,同时我们的应用要接入多数据源,并且通过统一的数据访问模型来进行访问。数据库读写分离将所有的读操作全部引入到Slave这个数据库服务器,把所有的写操作全部引入到Master这个主库当中。如下图所示。
因为数据库读写分离了,所以我们的应用程序也要做出相应的变化,我们在应用服务器中就实现了数据访问模块, 使得写代码的人不知道读写分离的存在,这样我们多数据源的读写对数据代码就没有了侵入。这里又引出了代码层次的一个演变,如何支持多数据源?如何封装对业务没有侵入?等等等等。
当访问量非常大的时候,数据库的读写分离又会遇到以下问题。例如,主库和从库在复制的时候,有没有延时。如果主库和从库分机房部署的话,跨几房传输同步数据更是一个问题。另外一个就是我们应用对于数据源的路由问题,这些都是我们要思考和解决的点。
我们增加了CDN和反向代理服务器,如下图所示。
使用CDN可以很好的解决不同的地区访问速度问题,反向代理则在机房中可以缓存用户的资源。
这个时候,我们的文件服务器又出现了一个瓶颈,我们继续演进。我们将文件服务器又改成了分布式文件服务器集群,在使用分布式文件系统的时候,我们要考虑以下几个问题:如何不影响已部署在线上的业务访问,我们不能让某个图片突然访问不到。是否需要业务部门帮忙清洗数据?是否需要备份服务器?是否需要重新做域名解析等等。
这个时候,我们的数据库又出现了一个瓶颈,我们选择使用专库专用的方式来解决,进行一个数据的垂直拆分,相关的业务都用自己的一个库,我们解决了写数据并发、量大的问题。
当我们把这些数据表分成不同的库,又会带来哪些新问题呢?
例如,我们跨库的事务如何解决呢?
我们可以使用分布式事务,或者去掉事务,或者不追求强事务。
随着我们访问量、数据量的扩大,我们某个业务的数据库中的数据量和更新量已经达到单个数据库的瓶颈了,这个时候我们就需要进行数据库的水平拆分。
如下图所示,我们将Users拆分成了User11和User12:
所谓的水平拆分,就是将同一个表的数据拆分到两个数据库当中,这个时候我们解决了单数据库的一个瓶颈。那么我们在水平拆分的时候,要注意哪些点呢?都有哪些水平拆分的方式呢?同样是需要我们思考的问题。
(要思考的好多)
进行水平拆分之后,我们又会碰到几个问题。
(碰到的问题也好多呀)
第一个就是SQL路由的问题,假如有一个User用户,我们怎么知道这个用户的信息是存在Users1数据库中还是Users2数据库中呢?
第二个是,因为已经分库了,我们组建的策略也会有所不同,同时也会面临一个分页的问题,假设我们系统中需要查询2017年四月份已经下单过的用户的明细,而这些用户又分布在Users1和Users2当中,我们后台的运营管理系统对它进行展示的时候,需要进行一个分页。
当我们完成这个架构之后会发现,应用服务器上的搜索量飙升,因为我们对外做了一些推广,然后我们继续演进,到这个时候呢,我们把应用服务器当中的搜索功能单独抽取出来,做了一个搜索引擎,同时我们的部分场景可以使用NoSQL提高性能,我们还会开发一个数据统一的访问模块,这个模块下面连着数据库集群、搜索引擎、NoSQL,解决上层应用开发的数据源问题。如下图所示:
当然了,这只是一个举例,各个服务的技术架构是需要根据自己的业务特点进行优化和演进的,所以呢,这个过程也不是完全相同的。这个架构也不是最终的形态,这个架构还是存在很多要提升的地方。比如我们的负载均衡服务器(Load Balancing)是一个单点,如果这个负载均衡服务器访问不了,那么我们的服务器集群等等等等也都访问不了,所以我们可以将它也做成一个集群。
所以,一个好的架构都是不断演进的,根据当前所遇到的问题,有针对性的进行优化和演进。这个图就不往下画啦,对于业务场景和性能需求,它也会越来越完善的。
我们继续往下看,这个过程中,我们还要关注:安全性、数据分析、监控、反作弊等等。
我们的架构继续发展下去,会发展成SOA架构、服务化、增加消息队列、增加任务调度、进行多机房部署,等等。
所以,高大上的项目技术架构和开发设计实现不是一蹴而就的。刚刚我们过了一遍在一个网站架构演进的过程中,大到系统架构,小到具体的代码,这些都会随之不断优化和演进。
啊~终于听完这节了,十分枯燥,十分听不懂,就当吃第一个大饼饼吧,虽然不懂,虽然感觉不到它的意义,但是既然花时间去听了,肯定有它的意义。