亿级流量网站架构核心技术-高并发原则-之缓存银弹的体系化玩法

下面是一个典型的雪崩应用场景:

那么有什么体系化的解决办法吗?

缓存银弹

web服务常见的缓存架构:

①、客户端缓存

1、浏览器缓存

2、客户端应用缓存

②、客户端网络

1、代理服务器开启缓存

③、广域网

1、代理服务器,如CDN

2、使用镜像服务器

3、使用 P2P 技术

④、源站及源站网络

1、使用接入层提供的缓存机制,如nginx、lua

2、使用应用层提供的缓存机制

3、使用分布式缓存

4、静态化、伪静态化

5、使用服务器操作系统提供的缓存机制

客户端本地缓存 -- cdn -- nginx -- 服务器本地缓存 -- 分布式缓存

①、客户端缓存之浏览器端缓存

设置请求的过期时间,如对响应头 expires、cache-control 进行控制。

这种机制适合于实时性不强的数据,如商品详情页框架、商家评分、评价、广告词等。

但是对于价格、库存等要求较高的数据,就不能做浏览器缓存。

②、APP 客户端缓存

在大促时为了防止瞬间流量冲击,一般会在大促之前把APP需要访问的一些素材 (如js/css/image等)提前下发到客户端进行缓存。

这样在大促时就不用去拉取这些素材了。

还有如首屏数据也可以缓存起来,在网络异常情况下还是有托底数据给用户展示。

还有如APP地图一般也会做地图的离线缓存。

③、cdn缓存

有些网页、活动页、图片等服务可以考虑将页面、活动页、图片等推送到离用户最近的cdn节点,让用户能在离他最近的节点找到想要的数据。

一般有两种机制:

A、推送机制

当内容变更后主动推送到 CDN 边缘节点。

B、拉取机制

先访问边缘节点,当没有内容时,回源到源服务器拿到内容并存储到节点上。

两种方式各有利弊。

使用cdn原则

使用cdn时要考虑URL的设计,比如URL中不能有随机数,否则每次都穿透cdn回源到源服务器,相当于cdn没有任何效果。

对于爬虫,可以返回过期数据而选择不回源。

④、接入层缓存

对于没有cdn缓存的应用来说,可以考虑使用如Nginx 搭建一层接入层,该接入层可以考虑使用如下机制实现:

1、URL 重写

将URL按照指定的顺序或者格式重写,去除随机数。

2、一致性hash

按照指定的参数(如分类/ 商品编号)做一致性hash,从而保证相同数据落到一台服务器上。

3、proxy_cache:

使用内存级/SSD 级代理缓存来缓存内容。

4、proxy_cache_lock:

使用lock机制,将多个回源合并成一个,以减少回源量,并设置相应的lock超时时间。

5、shared_dict:

如果架构使用了 nginx + lua 实现,则可以考虑使用 lua shared_dict 进行cache,最大的好处就是 reload 缓存不会丢失。

此处要注意:

对于托底的数据或者异常数据,不应该让其缓存,否则用户在很长一段时间内看到这些数据。

⑤、应用层缓存

我们使用Tomcat ,可以使用堆内缓存 / 堆外缓存

堆内缓存:

最大的问题就是重启时内存中的缓存会丢失,此时流量风暴来临,则有可能冲垮应用。

还可以考虑使用 local Redis cache 来代替堆外内存。

或在接入层使用shared_dict来讲缓存前置,以减少风暴。

local Redis cache

通过在应用服务器上部署一组Redis,应用直接读取本机Redis获取数据,多机器之间使用主从机制同步数据。

优点: 这种方式没有网络消耗,性能是最优的。

⑥、分布式缓存

上面说到了缓存的体系架构,一个常见的问题:

A、数据量不大:采用应用 local Redis cache 架构时最优的。

B、数据量太大:单服务器存储不了,那么需要使用分片机制将流量分配到多台机器,或者直接用分布式缓存实现。

而常见的分片机制就是一致性hash了。(想一想为什么是一致性hash?如何实现一致性hash?)

下面是一个常见的应用缓存架构:

1、首先,接入层 ( Nginx + Lua )读取本地 proxy cache / local cache。

其中,Twemproxy是一种代理分片机制,由Twitter开源。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。

2、如果不命中,则接入层会接着读取分布式Redis集群。

3、如果还不命中,则会回源到Tomcat,然后读取Tomcat应用堆内cache。

4、如果以上都没命中,则调用依赖业务来获取数据,然后异步化写入到Redis。(思考,以上各层没命中的原因是什么?有什么手段提升各层的命中率?)

如果使用的了Nginx + Lua ,二三步可以使用 lua-resty-lock 非阻塞锁减少峰值时的回源量(提交命中率);

如果你的服务是用户维度的,那么这种非阻塞锁大部分情况下不会有太大的作用。

缓存银弹大法到这里就告一段落咯

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190211G0N1SV00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券