通常的电商演变:html
商品详情页系统架构演进历程前端
第一个版本
架构设计
J2EE+Tomcat+MySQL
动态页面,每次请求都要调用多个依赖服务的接口,从数据库里查询数据,而后经过相似JSP的技术渲染到HTML模板中,返回最终HTML页面
架构缺陷
每次请求都是要访问数据库的,性能确定不好
每次请求都要调用大量的依赖服务,依赖服务不稳定致使商品详情页展现的性能常常抖动
第二个版本
架构设计
页面静态化技术
经过MQ获得商品详情页涉及到的数据的变动消息
经过Java Worker服务全量调用全部的依赖服务的接口,查询数据库,获取到构成一个商品详情页的完整数据,并经过velocity等模板技术生成静态HTML
将静态HTML页面经过rsync工具直接推送到多台nginx服务器上,每台nginx服务器上都有全量的HTML静态页面
nginx对商品详情页的访问请求直接返回本地的静态HTML页面
在nginx服务器前加一层负载均衡设备,请求打到任何一台应用nginx服务器上,都有全量的HTML静态页面能够返回
架构缺陷
全量更新问题
若是某一个商品分类、商家等信息变动了
那么那个分类、店铺、商家下面全部的商品详情页都须要从新生成静态HTML页面
更新速度过慢问题
分类、店铺、商家、商品愈来愈多
从新生成HTML的负载愈来愈高,rsync全量同步全部nginx的负载也愈来愈高
从数据变动到生成静态HTML,再到全量同步到全部nginx,时间愈来愈慢
扩容问题
由于每一个商品详情页都要全量同步到全部的nginx上,致使系统没法扩容,没法增长系统容量
架构优化
解决全量更新问题
每次Java Worker收到某个维度的变动消息,不是拉去全量维度并生成完整HTML,而是按照维度拆分,生成一个变化维度的HTML片断
nginx对多个HTML片断经过SSI合并html片断而后输出一个完整的html
解决扩容问题
每一个商品详情页不是全量同步到全部的nginx
而是根据商品id路由到某一台nginx上,同时接入层nginx按照相同的逻辑路由请求
更新速度过慢问题
增长更多机器资源
多机房部署,每一个机房部署一套Java Worker+应用Nginx,全部机房用一套负载均衡设备,在每一个机房内部完成全流程,不跨机房
架构优化后的缺陷
更新速度仍是不够快的问题
商品的每一个维度都有一个HTML片断,rsync推送大量的HTML片断,负载过高,性能较差
Nginx基于机械硬盘进行SSI合并,性能太差
仍是存在全量更新的问题
虽然解决了分类、商家、店铺维度的变动,只要增量从新生产较小的HTML片断便可,不用全量从新生成关联的全部商品详情页的HTML
可是若是某个页面模板变动,或者新加入一个页面模板,仍是会致使几亿个商品的HTML片断都要从新生成和rsync,要几天时间才能完成,没法响应需求
仍是存在容量问题
nginx存储有限,不能无限存储几亿,以及增加的商品详情页的HTML文件
若是nginx存储达到极限,须要删除部分商品详情页的HTML文件,改为nginx找不到HTML,则调用后端接口,回到动态页面的架构
动态页面架构在高并发访问的状况下,会对依赖系统形成过大的压力,几乎扛不住
第三个版本
须要支持的需求
迅速响应各类页面模板的改版和个性化需求的新模板的加入
页面模块化,页面中的某个区域变化,只要更新这个区域中的数据便可
支持高性能访问
支持水平扩容的伸缩性架构
架构设计
系统架构设计
依赖服务有数据变动发送消息到MQ
数据异构Worker服务监听MQ中的变动消息,调用依赖服务的接口,仅仅拉取有变动的数据便可,而后将数据存储到redis中
数据异构Worker存储到redis中的,都是原子未加工数据,包括商品基本信息、商品扩展属性、商品其余信息、商品规格参数、商品分类、商家信息
数据异构Worker发送消息到MQ,数据聚合Worker监听到MQ消息
数据聚合Worker将原子数据从redis中取出,按照维度聚合后存储到redis中,包括三个维度
基本信息维度:基本信息、扩展属性
商品介绍:PC版、移动版
其余信息:商品分类、商家信息
nginx+lua,lua从redis读取商品各个维度的数据,经过nginx动态渲染到html模板中,而后输出最终的html
如何解决全部的问题
更新问题:再也不是生成和推送html片断了,再也不须要合成html,直接数据更新到redis,而后走动态渲染,性能大大提高
全量更新问题:数据和模板分离,数据更新呢就更新数据,模板更新直接推送模板到nginx,不须要从新生成全部html,直接走动态渲染
容量问题:不须要依赖nginx所在机器的磁盘空间存储大量的html,将数据放redis,html就存放模板,大大减小空间占用,并且redis集群可扩容mysql
商品详情页总体架构组成nginx
动态渲染系统
将页面中静的数据,直接在变动的时候推送到缓存,而后每次请求页面动态渲染新数据
商品详情页系统(负责静的部分):被动接收数据,存储redis,nginx+lua动态渲染
商品详情页动态服务系统(对外提供数据接口)
提供各类数据接口ajax
动态调用依赖服务的接口,产生数据而且返回响应
从商品详情页系统处理出来的redis中,获取数据,并返回响应
OneService系统
动的部分,都是走ajax异步请求的,不是走动态渲染的
商品详情页统一服务系统(负责动的部分)
前端页面
静的部分,直接被动态渲染系统渲染进去了
动的部分,html一到浏览器,直接走js脚本,ajax异步加载
商品详情页,分段存储,ajax异步分屏加载
工程运维
限流,压测,灰度发布redis
多级缓存架构
本地缓存
使用nginx shared dict做为local cache,http-lua-module的shared dict能够做为缓存,并且reload nginx不会丢失
也可使用nginx proxy cache作local cache
双层nginx部署,一层接入,一层应用,接入层用hash路由策略提高缓存命中率
好比库存缓存数据的TP99为5s,本地缓存命中率25%,redis命中率28%,回源命中率47%
一次普通秒杀活动的命中率,本地缓存55%,分布式redis命中率15%,回源命中率27%
最高能够提高命中率达到10%
全缓存链路维度化存储,若是有3个维度的数据,只有其中1个过时了,那么只要获取那1个过时的数据便可
nginx local cache的过时时间通常设置为30min,到后端的流量会减小至少3倍
4级多级缓存
nginx本地缓存,抗热点数据,小内存缓存访问最频繁的数据
各个机房本地的redis从集群的数据,抗大量离线数据,采用一致性hash策略构建分布式redis缓存集群
tomcat中的动态服务的本地jvm堆缓存
支持在一个请求中屡次读取一个数据,或者与该数据相关的数据
做为redis崩溃的备用防线
固定缓存一些较少访问频繁的数据,好比分类,品牌等数据
堆缓存过时时间为redis过时时间的一半
主redis集群
命中率很是低,小于5%
防止主从同步延迟致使的数据读取miss
防止各个机房的从redis集群崩溃以后,全量走依赖服务会致使雪崩,主redis集群是后备防线
主redis集群,采起多机房一主三从的高可用部署架构
redis集群部署采起双机房一主三活的架构,机房A部署主集群+一个从集群,机房B部署一个从集群(从机房A主集群)+一个从集群(从机房B从集群)
双机房一主三活的架构,保证了机房A完全故障的时候,机房B还有一套备用的集群,能够升级为一主一从
若是采起机房A部署一主一从,机房B一从,那么机房A故障时,机房B的一从承载全部读写压力,压力过大,很难承受sql
动态渲染那套系统数据库
(1)依赖服务 -> MQ -> 动态渲染服务 -> 多级缓存
(2)负载均衡 -> 分发层nginx -> 应用层nginx -> 多级缓存
(3)多级缓存 -> 数据直连服务后端
动态渲染系统
数据闭环
数据闭环架构
依赖服务:商品基本信息,规格参数,商家/店铺,热力图,商品介绍,商品维度,品牌,分类,其余
发送数据变动消息到MQ
数据异构Worker集群,监听MQ,将原子数据存储到redis,发送消息到MQ
数据聚合Worker集群,监听MQ,将原子数据按维度聚合后存储到redis,三个维度(商品基本信息、商品介绍、其余信息)
数据闭环,就是数据的自我管理,全部数据原样同步后,根据本身的逻辑进行后续的数据加工,走系统流程,以及展现k
数据造成闭环以后,依赖服务的抖动或者维护,不会影响到整个商品详情页系统的运行
数据闭环的流程:数据异构(多种异构数据源拉取),数据原子化,数据聚合(按照维度将原子数据进行聚合),数据存储(Redis)
数据维度化
商品基本信息:标题、扩展属性、特殊属性、图片、颜色尺码、规格参数
商品介绍
非商品维度其余信息:分类,商家,店铺,品牌
商品维度其余信息:采用ajax异步加载,价格,促销,配送至,广告,推荐,最佳组合,等等
采起ssdb,这种基于磁盘的大容量/高性能的kv存储,保存商品维度、主商品维度、商品维度其余信息,数据量大,不能光靠内存去支撑
采起redis,纯内存的kv存储,保存少许的数据,好比非商品维度的其余数据,商家数据,分类数据,品牌数据
一个完整的数据,拆分红多个维度,每一个维度独立存储,就避免了一个维度的数据变动就要全量更新全部数据的问题
不一样维度的数据,由于数据量的不同,能够采起不一样的存储策略
系统拆分
系统拆分更加细:依赖服务、MQ、数据异构Worker、数据同步Worker、Redis、Nginx+Lua
每一个部分的工做专一,影响少,适合团队多人协做
异构Worker的原子数据,基于原子数据提供的服务更加灵活
聚合Worker将数据聚合后,减小redis读取次数,提高性能
前端展现分离为商品详情页前端展现系统和商品介绍前端展现系统,不一样特色,分离部署,不一样逻辑,互相不影响
异步化
异步化,提高并发能力,流量削峰
消息异步化,让各个系统解耦合,若是使用依赖服务调用商品详情页系统接口同步推送,那么就是耦合的
缓存数据更新异步化,数据异构Worker同步调用依赖服务接口,可是异步更新redis
动态化
数据获取动态化:nginx+lua获取商品详情页数据的时候,按照维度获取,好比商品基本数据、其余数据(分类、商家)
模板渲染实时化:支持模板页面随时变化,由于采用的是每次从nginx+redis+ehcache缓存获取数据,渲染到模板的方式,所以模板变动不用从新静态化HTML
重启应用秒级化:nginx+lua架构,重启在秒级
需求上线快速化:使用nginx+lua架构开发商品详情页的业务逻辑,很是快速
多机房多活
Worker无状态,同时部署在各自的机房时采起不一样机房的配置,来读取各自机房内部部署的数据集群(redis、mysql等)
将数据异构Worker和数据聚合Worker设计为无状态化,能够任意水平扩展
Worker无状态化,可是配置文件有状态,不一样的机房有一套本身的配置文件,只读取本身机房的redis、ssdb、mysql等数据
每一个机房配置全链路:接入nginx、商品详情页nginx+商品基本信息redis集群+其余信息redis集群、商品介绍nginx+商品介绍redis集群
部署统一的CDN以及LVS+KeepAlived负载均衡设备浏览器
队列化
任务等待队列
任务排重队列(异构Worker对一个时间段内的变动消息作排重)
失败任务队列(失败重试机制)
优先级队列,刷数据队列(依赖服务洗数据)、高优先级队列(活动商品优先级高)
并发化
数据同步服务作并发化+合并,将多个变动消息合并在一块儿,调用依赖服务一次接口获取多个数据,采用多线程并发调用
数据聚合服务作并发化,每次从新聚合数据的时候,对多个原子数据用多线程并发从redis查询
redis: