nginx_lua异步更新缓存
NGINX
一直使用nginx作为反向代理服务器,一来nginx基于事件驱动,速度快。而来nginx的反向代理模块能很好的支持页面缓存和负载均衡。
页面缓存
nginx有proxy_cache这个内置的缓存功能,是基于文件的。如果把缓存路径设置到RAMDISK上面,可以达到和内存缓存差不多的缓存读写速度。这样做虽然解决了文件读写慢的问题,但是如果分布式部署的时候,这个缓存不能跨机器共享。
srcache-nginx-module
章大神写的这个模块支持和另一个模块配合将页面缓存写入redis集群。就很好的解决了
缓存不能跨机器共享的问题
也使得缓存大小不受单机资源限制。
同时,redis本身还能提供快速的读写速度。
问题
看上面列的几点,似乎已经很完美了。
但是在实际使用的时候还是发现有需要改善的地方:
缓存不存在的时候:
第一个请求查询redis失败,到后端,最后还要存储redis。比没有缓存的情况还多出两次redis访问,一次读一次写。而且当并发访问的时候还可能请求都会进入后端服务器,后端不够健壮的时候会导致“雪崩效应”。(如果使用默认的proxy_cache模块还能通过proxy_cache_use_stale命令避免,但是srcache模块没有实现类似功能)缓存失效的时候
通常我们为页面设置缓存时间,缓存失效的时候,需要重新更新缓存。这个时候也会出现第一种情况类似的问题。
以上两种情况,第一种出现较少,第二种情况出现较多。都是由于redis自动删除过期缓存,导致的缓存缺失。
解决办法
由于nginx不知道redis里面数据的缓存时间,所以会频繁的导致缓存缺失的情况出现。
既然知道原因了,那么咱们就来针对性的解决一下这个问题。让nginx能够知道甚至参与到缓存时间的管理里面,就能有效的避免被动的缓存缺失问题,能有意识的主动更新缓存。
ngx_lua
这里需要提到一个nginx的第三方模块就是把lua集成到nginx里面扩展了nginx配置文件语法,支持在配置文件里面直接使用lua语言编写逻辑。
lua-resty-cache
我将自己在项目中使用的一个仓库开源到github上面了。
能让nginx主动的检测缓存的过期时间
在快过期的时候,直接返回旧的缓存数据
使用异步任务更新缓存
以上的第二第三两点,就能在缓存快过期的时候,主动的异步更新缓存,让终端用户感知不到更新缓存的过程。
项目中实测,使用这个缓存机制之后,用户感知到的平均响应速度提升了10倍(nginx access log分析结果),而这都是在原有系统性能不变的情况下实现的!是不是有点不可思议。
原理
下面就来说一说提升速度的原理:
如果缓存缺失,直接跳过srcache_fetch步骤,将请求发到后端server。同时申请一个共享内存锁,并发而来的相同的请求不用发送到后端,而是等待这个请求返回。
更新缓存的时候,在过期时间expire基础上加上一个stale time。那么在到了过期时间的时候,redis还不会立即删掉这个数据。
如果检测到数据过期,但是redis还能拿到这个“过期”数据,则立即返回过期数据给终端用户。
在结束请求的同时,使用一个异步任务去更新缓存,考虑到并发情况,这里也需要使用一个共享内存锁。
这样,即使在缓存过期的时候,用户也很少会遇到只能从后端server拿数据的情况。所以能节省相当的时间,在速度上有这么大的提升。
后记
这个库运行了有好几个月了,非常的稳定。当然这得益于nginx的稳定。
但是也不得不说,使用这个库之后,后端server能够收到的请求,已经绝大部分都是由那个异步的任务发出的。用户基本都能在缓存里面直接拿到数据。所以,后端server的性能,没有提升,但是给用户的感觉却是快了很多!!!