现代社会网购已经成为我们生活的一部分,即使你很少网购,你也一定会因为家眷的网购需求而有所支出。所以你也一定对下面这种页面司空见惯,如图所示:
你一定会觉得,“这有啥?不就是个展示商品的页面吗?” 但请注意就这一张小小的页面,包含的数据信息可以说是极为丰富的。如图所示:
图中我用不同颜色将图划分了一些区域,每一块区域都代表了在这个商品展示页面里的一块数据维度:
- 蓝色 代表商家信息的数据维度,包括:商家名称,商家评分
- 绿色 代表产品的分类
- 红色 代表所浏览的商品信息 包括:商品标题,商品价格,商品分类
- 那么此时我们用屁股想想都应该知道这些数据肯定来自于多个数据源,从而才使得这一张页面的页面里所包含的数据如此的丰富。这种仅仅以一件单品信息为用户关注主体,却又包含如此丰富其他维度信息的页面,被称作商品详情页
2、商品详情页架构v1.0
正如前面所说,这些数据来自于多个数据源,如果我们用最传统的SSM架构来做这套系统大概是这样子的
如图所示:
这个架构十分简单,问题自然也就十分的明显:
问题一:
如图↓所示,每次用户对一个商品详情页进行访问,都会直接查询MySQL数据库,性能之差那是可想而知的,并且mysql抗并发能力是极其有限的,让它直接正面迎接巨额流量是极其危险的。
问题二:
预想一下mysql一旦耍起性子来玩起了罢工,就问你怕不怕?所以解放mysql是现在的当务之急,商品详情页系统与其依赖的数据源系统高度耦合了,但凡其中有一个依赖服务出现网络波动等外界因素,会直接导致商品详情页系统无法正常提供服务。如图↓所示:
由此可见,商品详情页系统与各个源数据系统解耦是我们必须做的事情。回头看看,这样的架构想要支撑起百万流量的商品详情页系统简直就是痴人说梦。
3、商品详情页架构v2.0
为了解决上述的两个问题,我们会一步一步的做改动:
思考一下,让mysql面临巨额流量的根本原因是什么?
对mysql直接发起请求,来查询用户需要的数据,对页面进行渲染。用户的需求仅仅是一份美观的数据详情页,用于浏览即可,往往对数据的时效性,要求并不是那么的严格。
所以我们可以这样,让公司内部的运营人员对商品详情页系统发起通知,当商品详情页系统接收到运营人员的通知,就去各个源数据系拉取数据,同于渲染html页面,渲染完成一个html页面就将其推送到一个ngnix服务器上。那么之后的用户直接从ngnix这一层就能浏览到商品详情页了。此时的架构变化如图↓所示:
此时可以看到真正对数据库访问的是我们的内部运营系统,数据库的负载自然小的不是一星半点,而用户在数据访问层的ngnix就能获取到商品详情页的内容了。
接着要解决商品详情页系统和各个数据源系统高度耦合的问题。
上文说到耦合的副作用就是当商品详情页系统访问各个数据源系统时一旦其中一个系统发生网络波动就直接影响到商品详情页对html的渲染。
那么为何不让这些数据源服务自己主动把最新的消息推送过来,此时商品详情页就只要关注html的渲染和页面的推送就可以了。就不需要“传话人”的运营系统了,岂不是美滋滋?
既然说到了消息推送,自然想到解耦那么对于MQ的引入也就随之呼之欲出了。按照这个思路我们可以做以下改动,如图↓所示:
4、改善架构2.0
经过一番改造系统的性能有了较大的改善,但是大家仔细看我下面对上图↑标记的 如图↓:
图中圈出的一块问题是显而易见的,就是只要任意维度的数据发生了变更,那么对应商品详情页就要进行完整的重新渲染,这无疑是极其浪费性能的。
由开头的电商截图可以知道,每一个维度的数据反应在商品详情页上其实就是在在一块各自的固定区域里填上相关的的内容而已,那么在,布局已经确立的情况下,布局已经确立的情况下,布局已经确立的情况下 ,重要的事情说三遍!!!
此时我们完全可以根据现在的商品详情页抽出一套模板,只要填充对应部分的数据即可,就像如图↓所示:
那么现在不管哪个维度发生改变,只要更新对应的商品详情页的维度所在区域即可,如图↓所示:
根据这个思路我们可以把html按照维度拆成一个一个的html片段,各个消费端只需要关注自己的维度,渲染和上传相关的html片段即可,如图↓所示:
但是现在又有一个新的问题!
上传到ngnix服务器上是一个一个的html片段,那么又如何将这些片段组装在一起成为一个完整的html页面呢?这时候我们就需要SSI动态内容缓存技术帮我们完成页面的“组装”工作,如图↓所示:
好了,咱们现在的页面也实现了局部渲染,问题看似得已解决了。
5、如上架构总结- SSM架构的问题
- ①Mysql服务器压力大
- ②返回环节若出现问题就导致无法服务
- 解决方案
- 数据分层
- 问题:主要一个层变化,就要重新渲染页面;消耗资源
- 通过增加人员,来发起数据库请求,分担请求直接打入数据库
- 数据分层
- 改进方案
- 使用消息中间件,MQ;从分担人员主动发起请求,变为被动接受请求;
- 通过SSI,将html拆为片段,数据纬度分层,让其局部渲染
6、扩容问题
我们现在案例是以百万流量大型电商的商品详情页为背景,那么不能只把眼光放在抗流量上,因为我们的数据访问层的ngnix实际上扮演了一个存储设备的角色,上面存放着电商网站所有的商品详情页的片段,
那么一个电商网站肯定有数以万计的商品给用户挑选,而且以后还得不断发布新的商品,以现在的架构迟早会发生如图↓所示的问题:
看到这,你可能要问这有何难?加个机器不就行了吗?是的看上去是这样的,
但是你怎么让你推送的页面,均匀的分配到各个ngnix上呢?如果想不清楚这个问题,就会出现如图↓的问题:
解决方案:
那么此时我们必须制定路由规则,来引导消费端将商品详情页平均的推送到各个ngnix 上,并且保证同一个商品的html片段必须分配到同一台ngnix服务器上,那么如图↓所示:
那么相对的也要保证用户访问的时候,也遵循和推送一致的路由算法进行页面定位,这个时候我们就要数据访问层的ngnix前面增设一台用于路由分发的ngnix,如图↓所示:
至此ngnix的扩容和路由定位的问题也被我们解决了。
总结:
- 问题:
- 每个html分段储存的nginx都因为空间满,而无法工作
- 解决方案:
- 让后台mq提供的数据根据一种规则将数据返给不同的nginx服务
- 让用户请求请求的nginx服务也根据一种规则来发送请求
7、架构3.0
现在的架构看上去十分完美,但是还是有一些问题的,回看一下,我在前面强调三遍的内容,因为强调的内容是能够使用SSI技术解决全量渲染的前题,再讨论这个问题之前大家先明确的知道一点那就是SSI技术在这里起到的作用是对html文件的组装
所以当整个骨架都发生变化的时候原先组装在这套模板里的所有html文件都要重新排列组合,那就必然会导致全量的重新渲染,场景大致如图↓:
那么现在问题就比较明显了,因为作为一套后端架构,他的性能直接与前端页面布局有一定的联系,这是比较不合适的,本质上我们只关心数据的填充,页面怎么布局我应该不需要care,况且ssi是基于磁盘让html进行组装,这个方案性能本身就不算多好。
那我们应该怎么才能即保证高性能的数据读取,又能保证高性能页面渲染呢?
就是说SSI技术不够好
既然提到高性能的数据读取,自然就该轮到我们的主角redis登场了,那我们有什么技术手段,能让ngnix加载到redis中的数据用,并且渲染到html页面里呢?
利用lua脚本以及动态渲染模板来完成这些功能。
并且此时我们不涉及到什么页面的推送,最终我们的架构大致就是如图↓所示:
-
商品详情页系统在接收到数据源的数据,
-
根据数据维度的不同将数据写入对应redis的中就可以了,
-
在用户发起请求路由到对应的商品详情页所在的ngnix上,
-
通过lua脚本从各个维度的redis中读取到对应的数据,
-
接收到数据之后 再由ngnix动态渲染到html模板中,再也不用囤积大量的html碎片了,
-
最终一个完整的html就这样展现在我们眼前了。
并且由于ngnix只存放页面模板,分发层起到的不仅是负载均衡的作用,也保证了数据访问层ngnix的高可用·。



