Nginx+Redis+Ehcache大型高并发高可用三层架构总结
在生产环境中,对于高并发架构,我们知道缓存 是最重要的环节,对于大量的高并发。可以采用三层缓存架构来实现,也就是Nginx+Redis+Ehcache
对于中间件Nginx常来做流量分发,同事nginx本身也有自己的缓存机制,但是呢,容量也是有限,我们可以用来缓存热点数据,让用户的请求直接走缓存并返回,从而减少流向服务器的流量
一:模板引擎
通常可以配合使用freemaker/velocity等模板引擎来抗住打量的请求
小型系统可能直接在服务器端 渲染出所有页面并放入缓存,之后的相同页面 请求就可以直接返回,不用去查询数据源或者做数据逻辑处理
二:双层nginx来提升缓存命中率
对于部署多个nginx而言,如果不加入 一些数据的路由策略,那么可能导致每个nginx的缓存命中率很低,因此可以部署双层Nginx
分发层nginx负责流量 分发的逻辑和策略,根据自己定义的一些规则,比如根据productld(产品表示)进行hash,然后对后端nginx数据访问 固定路由到一个nginx后端服务器上去,后端nginx用来缓存一些热点数据到自己的缓存区;
用户的请求,在nginx没有缓存相应数据,那么进入到redis缓存中,redis可做到全量数据的缓存,通过水平扩展能够提升高并发,高可用能力,
一:持久化机制
所谓持久化 机制就是讲redis内存中的数据持久化到磁盘中,然后可以定期讲磁盘文件上传到S3或 这云存储 服务上去
如果同时使用RDB和AOF两种持久化机制,那么在redis重启的时候,会使用AOF来重新构建数据,因为AOF中的数据更加完善,建议将两种持久化机制都开启,用AOF来保证数据不丢失,作为数据恢复的首选;而RDB用来做不同程度的冷备,在AOF文件都丢失或者损坏不可用的时候快速进行数据恢复
Ps:又一个坑需要踩,对于想从RDB恢复数据,同时AOF开关也是打开的,一直都无法正常恢复,因为每次会优先从AOF获取数据(如果临时关闭AOF,就正常恢复了)这个时候需要先停止redis,然后在关闭AOF,拷贝RDB到相应的目录,启动redis之后热修改配置参数redis config set appendonly yes,此时会自动生成一个当前内存数据的AOF文件,然后再次停止redis,打开AOF配置,在此启动数据及正常启动了,
RDB:对于redis中的数据执行周期性的持久化,每一刻持久化都是全量的数据一个快照,对于redis性能影响较少,基于RDB能够快速的异常恢复
AOF:以append-only的模式写入一个日志文件中,在redis重启的时候 可以通过回放AOF日志中的写入指令来重新构建整个数据集(实际上每次写的日志数据会先到linux OS cache的数据写入磁盘)对redis有一定的影响,能够尽量保证数据的完整性,redis通过rewrite机制来保障AOF文件不会太大,基于内存数据并做到适当的指令重建
二:redis集群
replication
一主多从架构,主节点负责写入,并且将数据同步到其他salve节点(异步执行)从节点负责读,主要是用来做读写分离的横向扩容架构,这种架构的master节点数据一定要做持久化,否则当master宕机重启之后内存数据清空,那么就会将空数据复制到slave,导致所有数据丢失
sentinal哨兵
哨兵是redis集群架构中很重要的一个组件,负责监控redis master和slave进程是够正常工作,当某个redis实例出现故障时,能够发送告警通知到管理员,当master node宕机能够自动转移到slave node上
前两种架构方式最大的特定是,每个节点的数据是相同的,无法存取海量的数据,因此,哨兵集群的方式使用与数据量不大的情况
redis cluster
redis cluster支撑多master node,每个master node可以挂载多个slave node,如果master挂掉,会自动将对应的某个slave切换成master,需要注意的是redis cluster下slave节点主要是用来做高可用,故障主备切换的,如果一定需要slave能够提供读的能力,修改配置也可以实现(同时也需要修改jedis源码来支持该情况下的读写分离操作),slave节点能够自动迁移(让master节点尽量平均拥有slave节点)对整个架构过载冗余的slave就可以保障系统更高的可用性
Tomcat jvm堆内存缓存,主要是抗redis出现大规模灾难,如果redis出现了大规模的宕机,导致nginx大量流量直接永祥数据生产服务器,那么最后tomcat堆内存缓存也可以处理部分请求,避免所有请求都直接流向数据库;
【缓存数据更新策略】
对时效性要求高的缓存数据,但发生变更的时候,直接采取数据库和redis缓存双写方案,提高缓存时效性
对时效性不高的数据,当发生变更之后,采用MQ异步通知的方式,通过数据生产服务来监听MQ消息,然后异步去拉去服务的数据更新tomcat jvm和redis缓存,对于nginx本地缓存过之后就可以从redis中拉去新的数据并更新到nginx本地
【数据库和redis缓存双写不一致的问题】
将数据库与缓存更新的读写操作进行异步串行化,当跟新数据的时候,根据数据的唯一标识,将更新数据操作路由到一个jvm内部队列中,一个队列对应一个工作线程,线程串行随后在队列里一条一条的执行,当执行队列中的更新数据操作,删除缓存,然后去更新数据库,此时还没有完成更新的时候过来一个读请求,读到了空缓存,那么可以先将缓存更新之后的请求发送到路由之后的队列中,此时会在队列积压,然后同步等待缓存更新完成,
【缓存雪崩解决方案】
redis集群彻底奔溃,缓存服务大量对redis的请求等待,占用资源,随后缓存服务大量的请求进入源头服务去查询DB,使得DB压力过大直至崩溃,此时对源头的请求 也大量等待占用资源,缓存服务大量的资源全部消耗在访问redis和原服务无果,最后使得自身也无法提供服务,最终整个网站崩溃;
事前解决方案:搭建一套高可用架构redis cluster集群,主从架构,一主多从,并且最好使用双机房部署集群
事中解决方案:部署一层ehcache缓存,在整个redis集群中能够扛得住部分压力,对redis cluster的访问做资源隔离,避免所有资源都请求等待,对redis cluster的访问失败这种情况应该实施熔断策略,对源服务访问进行限流以及资源隔离
事后解决方案:redis数据做了备份可以直接恢复,重启redis即可,redis数据彻底丢失或者数据过旧,可以快速缓存预热,然后让redis重新启动,最后由于资源隔离的half-open策略发现redis恢复正常,那么所有的请求将自动恢复
【Nginx缓存失效导致redis压力倍增】
这个时候我们可以在nginx本地设置缓存数据的时候设置缓存有效期,避免同一时间缓存都失效导致大量的请求直接进入redis