QPS第二级削峰—流量网关+Nginx+lua+Redis+模板引擎
LVS+Nginx动静分离
上文聊到,对用户入口流量的第一级控制,其实就是DNS智能解析,搭配一个负载均衡器LVS或Nginx,配合Keepalived做到入口高可用,代理或转发请求到Nginx节点,做负载均衡,并从Nginx节点上获取 html资源。
但此时需要思考一个问题?
在html上请求到的都是静态资源,即页面上不变的资源,而动态需要变化的数据比如价格,实时的库存,商家上新的型号等等怎么处理?
Nginx动静分离或许是个OK的答案?
Nginx动静分离,即对静态资源的请求直接由Nginx这一层做响应,动态请求负载均衡到后端Tomcat
但对于双十一的高并发流量,所有动态请求都打到Tomcat是否合适?
我们希望在流量打到后端Tomcat前,有办法在前面响应到这部分动态数据,那么能想到的是流量在Tomcat前经过了CDN和LVS+Nginx
我们首先试图从CDN着手解决问题
我们希望在CDN也能存储页面的一些动态数据,以页面渲染解析能接受的JSON格式存在,这样流量对于动态数据的请求就能在CDN这一层解决,而这部分JSON格式的动态数据来源,只能是来自于能访问 DB数据库的后台服务,如下图
1、面向商家的商品服务,修改商品的动态数据如价格,库存,型号等,编辑完成后触发投递修改数据到MQ
2、文件服务消费MQ后,生成json文件,将这部分商品数据同步到源站存储,同时清除CDN上的文件缓存
3、这样客户对于动态数据的请求,也能在CDN侧做到流量的控制,不会直接打到后端Tomcat,浏览器在从CDN取到动态数据的json文件后,完成解码,取数和渲染
什么样的系统适合这样的方案?
1、价格不常变化的——价格变化需要重新生成Json文件,同步到CDN源站,并且清除原有CDN缓存
2、商品数量不多——如果是淘宝&京东亿级数量的商品,则CDN既需要缓存静态资源文件,又得缓存N*亿级的Json文件,压力会很大
此架构比较适合:
旅游电商网站——价格按旅游旺季和淡季变化,旅游商品的套餐不会太多
瓜子二手车——价格车主定价后一般改动不大,二手车市场也不会有亿级的数量级
那么如果是淘宝和京东这样双十一高并发的流量,需要怎样的架构才合适?
我们思考下,除了CDN,在流量打到Tomcat前,还有一道Nginx,那么Tomcat前面一层的Nginx能做啥?
1、存放html,css,js等静态资源文件
2、写Lua脚本实现带业务逻辑的流量分发等...
3、做请求的反向代理
4、利用tmpfs做磁盘和内存的映射
而LVS+Nginx集群的架构,可以承载住一个地区范围内很高流量的并发,
基于此,我们想到是否能在后台Tomcat实时生成Html文件,即从DB查询到动态数据,填充到模板中,返回到Nginx中缓存,Nginx中开启可映射磁盘和内存的tmpfs,缓存html文件,下次再有请求访问,则直 接从Nginx这一层响应请求,这样基于LVS+Nginx的结构,分担了CDN的大部分压力,看似是可取的。
这个解决方案存在的三个问题
1、生成文件并响应的过程是否会很耗时?
2、如果所有页面的请求都打到一台Nginx上,则这个Nginx要承担全量Html页面的缓存,Nginx扛得住这么多文件的缓存吗?
3、Nginx上html文件的治理问题,比如更新,修改,页面过期的删除
所以我们需要再往一个方向去思考,有没有不需要生成文件的,不需要文件治理的,Nginx上能均匀的散落html文件的方案。
终极解决方案:Nginx+Lua+redis+模板引擎+热点数据缓存
架构优化的设计:
1、Nginx支持内嵌Lua脚本做Lua_lru_cached缓存,Lua从Redis拉取热点数据,比如Redis有1亿条数据,Nginx集群有50台,我们希望每台Nginx能承载200w的热点数据,而不是全量热点数据
Nginx1:1-200w的商品信息
Nginx2:200w-200w的商品信息
...
Nginx50:
将热点数据从Redis取回后,通过Lua的Json操作,把数据提前填充到Html模板文件,形成一个完整的Html文件。
2、如果LVS的负载均衡策略为IP哈希,那么假设商品id=30的请求打过来,通过IP哈希可能负载到多台不同的Nginx服务器上,这里导致的问题可能有:
(1)Nginx没有这件商品数据的话,每次都得去Redis拉取,造成了穿透
(2)Nginx上的数据一致性问题,比如Nginx节点1的商品id=1的数据和Nginx节点2的数据商品id=1的数据缓存失效时间不同,那么LB打到两台Nginx上得到的数据就不一致了
我们希望在LVS这一层做编程,实现对商品id的哈希,使之均匀的分布到Nginx服务器上,所以需要把LVS这一层替换成可编程的流量网关kong或openresty,这两者都是基于Nginx封装的高性能网关
3、对于写请求,比如下单,支付等操作,可通过Nginx上的Lua编程,扔到消息队列RocketMQ上去,通过异步操作和后端微服务消费保证数据的最终一致性
4、不同地区数据中心的数据一致性,通过地区拉专线来保证