缓存-高并发架构设计-限流和升级的利器 (缓存解决高并发)
软件系统有三个谋求:高性能、高并发、高可用,俗称三高。本篇探讨高并发,从高并发是什么到高并发应答的战略、缓存、限流、升级等。
引言
1.高并发背景
互联网行业迅速开展,用户量剧增,系统面临渺小的并发恳求压力。
软件系统有三个谋求: 高性能、高并发、高可用 ,俗称三高。三者既有区别也有咨询,门门道道很多,片面探讨须要三天三夜,本篇探讨高并发。
2.高并发对系统的应战
性能降低、资源竞争和稳固性疑问等。
一、什么是高并发
1.高并发的定义
高并发是指系统或运行程序在同一时期段内接纳到少量并发恳求的才干。详细来说,高并发环境下系统须要能够同时处置少量的恳求,而不会出现性能疑问或照应提早。
2.高并发的特点
3.高并发场景和运行
高并发场景宽泛运行于抢手网站、电商平台、社交媒体等互联网运行中。例如,在电商平台上有少量用户同时阅读、搜查商品,提交订单等操作;社交媒体平台上有少量用户同时颁布、点赞、评论等操作。这些场景须要系统能够同时处置少量恳求,并保证系统的性能、可用性和用户体验。
4.高并发的影响
二、高并发应答战略
三、缓存
1.简介
在网站或的开发中,缓存机制是一个无法或缺的环节,可以提高网站或APP的访问速度,降低数据库压力。在高并发环境下,缓存机制的作用愈加清楚,不只可以有效减轻数据库的负载,还可以提高系统的稳固性和性能,从而给用户带来更好的体验。
2.上班原理
缓存的上班原理是先从缓存中失掉数据,假设有数据则间接前往给用户,假设没有数据则从慢速设施上读取实践数据并且将数据放入缓存。
3.罕用技术
1)阅读器缓存
简介: 阅读器缓存是指将网页中的资源(如、、Script、图像等)存储在用户的阅读器外部,以便在后续恳求同一资源时可以间接从本地缓存中失掉,而无需再次从主机下载。
实用场景: 阅读器缓存实用于那些静态内容变动较少的网页和静态资源,可以清楚优化网站性能和用户体验,并缩小主机的负载。
经常出现用法:
经常使用阅读器缓存可以经过设置照应头中的Expires和Cache-Control字段来控制缓存的行为。
留意事项
阅读器缓存存储实时性不敏感的数据,如商品框架、商家评分、评估和广告词。它有过时时期,并经过照应头启动控制。实时性要求高的数据不适宜经常使用阅读器缓存。
2)客户端缓存
简介: 客户端缓存是将数据存储在阅读器中,以提高访问速度和缩小主机恳求。
实用场景: 在大促时期,为了防止服务端接受瞬间的高流量压力,可以提早将一些素材(如js/css/image等)下发到客户端启动缓存,防止在大促时期再次恳求这些素材。此外,还可以将一些兜底数据或样式文件寄存在客户端缓存中,以确保在服务端意外或网络意外的状况下,坚持app的反常运转。
3)CDN缓存
简介:
CDN(ContentDeliverywork)是建设在承载网之上的散布式网络,由散布在不同区域的边缘节点主机组成。
CDN缓存通罕用于寄存静态页面数据、优惠页面、图片等数据。它有两种缓存机制:推送机制(将数据被动推送到CDN节点)和拉取机制(初次访问时从源主机失掉数据并存储在CDN节点)。
实用场景: CDN缓存可以提高网站访问速度,实用于网站访问量大、访问速度慢、数据变动不频繁的场景。
罕用工具以及用法
经常出现的CDN缓存工具包含Cloudflare、Akam、Fastly和AWSCloudFront等。这些工具提供了世界散布的CDN网络,以减速内容传输和优化性能。它们提供了控制台和API,用于性能CDN缓存规定、治理缓存内容、刷新和升级缓存等。
4)反向代理缓存
简介: 反向代理缓存是指在反向代理主机上对恳求的照应启动缓存,以提高服务的性能和用户体验。它将经常恳求的静态内容缓存在代理主机上,当有用户恳求雷同的内容时,代理主机会间接前往缓存的照应,而无需再次向源主机恳求。
实用场景: 实用于访问外部服务速度比拟慢,然而 数据变动不频繁 的场景。
罕用工具以及用法
5)本地缓存
简介: 本地缓存是将数据或资源存储在客户端的存储介质中,如硬盘、内存或数据库。它可以是暂时的,只在运行程序运转时期有效,或许可以是耐久的,即在不同的运行程序会话中坚持有效。
实用场景: 本地缓存实用于 频繁访问数据 、离线访问、缩小带宽消耗和优化用户体验的场景。
罕用工具以及用法: 普通分为磁盘缓存、CPU缓存、运行缓存。
6)散布式缓存
简介: 散布式缓存是将缓存数据扩散存储在多台主机上的缓存处置打算。
实用场景: 高并发读取、数据共享和协同处置、提供弹性和可裁减性、降低后端恳求次数等场景。
罕用工具以及用法
4.缓存疑问
1)缓存穿透
关键词: 强调缓存和数据库都没有数据+并发访问
缓存穿透是指数据库缓和存都没有的数据,每次都要经过缓存去访问数据库,少量的恳求有或许造成DB宕机。
应答战略
2)缓存击穿
关键词: 强调单个热点Key过时+并发访问
缓存击穿是指数据库有,缓存没有的热点数据,少量恳求访问这个缓存不存在的数据,最后恳求打到DB或许造成DB宕机。
应答战略
3)缓存雪崩
关键词: 强调批量Key过时+并发访问
缓存雪崩指的是在同一时段少量的缓存键(key)同时失效,造成少量恳求打到数据库,最后恳求打到DB或许造成DB宕机。
应答战略
4)缓存分歧性
缓存分歧性指的是缓存与DB之间的数据分歧性,咱们须要经过各种手腕来防止缓存与DB不分歧,咱们要保证缓存与DB的数据分歧或许数据最终分歧。
应答战略
针对缓存分歧性疑问,可以从不同的档次来应答:
数据库层:
缓存层:
运行层:
监控和报警:
综合经常使用以下档次的战略,可以有效地应答缓存分歧性疑问,保证数据的分歧性和系统的稳固性。不同档次的战略可以相互配合,构成一个完善的缓存分歧性处置打算。
5)其余
缓存的好处咱们十分受益,用户的每一次性恳求都随同着有数缓存的降生,然而缓存同时也给咱们带来了不小的应战,比如在上方提到的一些疑难课题:缓存穿透、缓存击穿、缓存雪崩缓和存分歧性。
除此之外,咱们还会触及到其余的一些缓存难题,如:缓存歪斜、缓存阻塞、缓存慢查问、缓存主从分歧性疑问、缓存高可用、缓存缺点发现与缺点复原、集群扩容收缩、大Key热Key……
6)小结
以上是对阅读器缓存、客户端缓存、CDN缓存、反向代理缓存、本地缓存和散布式缓存的横向对比,包含引见、处置打算/工具、优势和缺陷以及实用场景的详细信息。依据详细需求和系统架构,选用适宜的缓存类型和打算,以提高系统性能、减轻主机负载、改善用户体验和保证数据分歧性。
四、限流
1.简介
再弱小的系统,也怕流量短事情内集中迸发,就像银行怕挤兑一样,所以,高并发另一个必无法少的模块就是限流。
限流是一种经过控制恳求的速率或数量来包全系统免受过载的技术。流控的精髓是限度单位时期内的恳求量,最大水平保证系统的牢靠性及可用性。
2.作用
限流是在高并发环境下,为了包全系统的稳固性和可用性而引入的一种战略。经过限度并发恳求的数量或频率,可以防止系统被过多的恳求压垮或耗尽资源。
3.限流算法
经常出现的流控算法包含:固定窗口、滑动窗口、漏桶、令牌桶、滑动日志等算法。
1)固定窗口算法(计数器)
简介: 固定窗口限流算法(FixedWindowRateLimitingAlgorithm)是一种最繁难的限流算法,其原理是在固定时期窗口(单位时期)内限度恳求的数量。
原理:
固定窗口是最繁难的流控算法。即,给定 时期窗口 ,保养一个 计数器 用于统计访问次数,并成功以下规定:
实用场景:
成功形式
publicclassFixedWindowRateLimiter{
privatestaticintcounter=0;//统计恳求数
privatestaticlonglastAcquireTime=0L;
privatestaticfinallongwindowUnit=1000L;//假定固定时期窗口是1000ms
privatestaticfinalintthreshold=10;//窗口阀值是10
publicsynchronizedbooleantryAcquire(){
longcurrentTime=System.currentTimeMillis();//失掉系统以后时期
if(currentTime-lastAcquireTime>windowUnit){//审核能否在时期窗口内
counter=0;//计数器清零
lastAcquireTime=currentTime;//开启新的时期窗口
if(counter<threshold){//小于阀值
counter++;//计数器加1
returntrue;//失掉恳求成功
returnfalse;//超越阀值,无法失掉恳求
代码说明:
经常使用了一个静态的counter变量来记载恳求数量,lastAcquireTime变量记载上一次性失掉恳求的时期戳。windowUnit示意固定时期窗口的长度,threshold则示意在时期窗口内的恳求数阀值。
![限流和升级的利器](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
tryAcquire()方法经常使用了synchronized关键字来成功线程安保,在方法中启动以下操作:
优劣剖析
优势:
缺陷:
比如:假定限流阀值为5个恳求,单位时期窗口是1s,假设咱们在单位时期内的 前0.8-1s 和,区分并发5个恳求。只管都没有超越阀值,然而假设算内的,则并发数高达10,曾经超越单位时期1s不超越5阀值的定义了。
2.滑动窗口算法
简介:
为了处置临界突变疑问,可以引入滑动窗口。即:把大的时期窗口拆分红若干粒度更细的子窗口,每个子窗口独立统计, 按子窗口时期滑动 ,一致限流。
当滑动窗口的格子周期划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越准确。
原理:
将单位时期周期分为 n个小周期 ,区分记载每个小周期内接口的访问次数,并且依据时期滑动删除过时的小周期。它可以处置固定窗口临界值的疑问。
假定单位时期还是,滑动窗口算法把它划分为5个小周期,也就是滑动窗口(单位时期)被划分为 5个小格子 。每格示意。每过0.2s,时期窗口就会往右滑动一格。而后呢,每个小周期,都有自己独立的计数器,假设恳求是抵达的,对应的计数器就会加1。
假定咱们1s内的限流阀值还是5个恳求,0.8~1.0s内(比如0.9s的时刻)来了5个恳求,落在黄色格子里。
时期过了1.0s这个点之后,又来5个恳求,落在紫色格子里。假设是固定窗口算法,是不会被限流的,然而滑动窗口的话,每过一个小周期,它会右移一个小格。过了1.0s这个点后,会右移一小格,以后的单位时期段是0.2~1.2s,这个区域的恳求曾经超越限定的5了,已触发限流啦,实践上,紫色格子的恳求都被拒绝。
成功形式
importjava.util.LinkedList;
importjava.util.Queue;
publicclassSlidingWindowRateLimiter{
privateQueue<Long>timestamps;//存储恳求的时期戳队列
privateintize;//窗口大小,即时期窗口内准许的恳求数量
privatelongwindowDuration;//窗口继续时期,单位:毫秒
publicSlidingWindowRateLimiter(intwindowSize,longwindowDuration){
this.windowSize=windowSize;
this.windowDuration=windowDuration;
this.timestamps=newLinkedList<>();
publicsynchronizedbooleantryAcquire(){
longcurrentTime=System.currentTimeMillis();//失掉以后时期戳
//删除超越窗口继续时期的时期戳
while(!timestamps.isEmpty()&¤tTime-timestamps.peek()>windowDuration){
timestamps.poll();
if(timestamps.size()<windowSize){//判别以后窗口内恳求数能否小于窗口大小
timestamps.offer(currentTime);//将以后时期戳参与队列
returntrue;//失掉恳求成功
returnfalse;//超越窗口大小,无法失掉恳求
代码解读
在以上代码中,经常使用了一个Queue(队列)来存储恳求的时期戳。结构函数中传入窗口大小windowSize和窗口继续时期windowDuration。
tryAcquire()方法经常使用了synchronized关键字来成功线程安保,在方法中启动以下操作:
经常使用这个滑动窗口限流算法,可以限度在肯定时期窗口内的恳求频率,超越窗口大小的恳求会被限度。您可以依据实践需求和业务场景启动调整和经常使用。
实用场景: 同固定窗口的场景,且对流量限度要求较高的场景, 须要更好地应答突发流量 。
优劣剖析
优势:
劣势:
3.漏桶算法
简介: 基于 (进口)流速 来做流控。在网络通讯中罕用于流量整形,可以很好地处置平滑度疑问。
特点:
原理
代码成功
publicclassLeakyBucketRateLimiter{
privatelongcapacity;//漏桶容量,即最大准许的恳求数量
privatelongrate;//漏水速率,即每秒准许经过的恳求数量
privatelongwater;//漏桶以后水量
privatelonglastTime;//上一次性恳求经过的时期戳
publicLeakyBucketRateLimiter(longcapacity,longrate){
this.capacity=capacity;
this.rate=rate;
this.water=0;
this.lastTime=System.currentTimeMillis();
publicsynchronizedbooleantryAcquire(){
longnow=System.currentTimeMillis();
longelapsedTime=now-lastTime;
//计算漏桶中的水量
water=Math.max(0,water-elapsedTime*rate/1000);
if(water<capacity){//判别漏桶中的水量能否小于容量
water++;//漏桶中的水量加1
lastTime=now;//升级上一次性恳求经过的时期戳
returntrue;//失掉恳求成功
returnfalse;//漏桶已满,无法失掉恳求
代码解读
在以上代码中,capacity示意漏桶的容量,即最大准许的恳求数量;rate示意漏水速率,即每秒准许经过的恳求数量。water示意漏桶中以后的水量,lastTime示意上一次性恳求经过的时期戳。
tryAcquire()方法经常使用了synchronized关键字来成功线程安保,在方法中启动以下操作:
实用场景
普通用于包全第三方的系统,比如自身的系统须要调用第三方的接口,为了包全第三方的系统不被自身的调用打垮,便可以经过漏斗算法启动限流,保证自身的流量颠簸的打到第三方的接口上。
优劣剖析
优势:
劣势:
4.令牌桶算法
简介: 基于(入口)流速来做流控的一种限流算法。
原理: 该算法保养一个固定容量的令牌桶,每秒钟会向令牌桶中放入肯定数量的令牌。当有恳求来到时,假设令牌桶中有足够的令牌,则恳求被准许经过并从令牌桶中消耗一个令牌,否则恳求被拒绝。
成功形式
importjava.util.concurrent.ScheduledExecutorService;
importjava.util.concurrent.ScheduledThreadPoolExecutor;
importjava.util.concurrent.TimeUnit;
publicclassTokenBucketRateLimiter{
privatelongcapacity;//令牌桶容量,即最大准许的恳求数量
privatelongrate;//令牌发生速率,即每秒发生的令牌数量
privatelongtokens;//以后令牌数量
privateScheduledExecutorServicescheduler;//调度器
publicTokenBucketRateLimiter(longcapacity,longrate){
this.capacity=capacity;
this.rate=rate;
this.tokens=capacity;
this.scheduler=newScheduledThreadPoolExecutor(1);
scheduleRefill();//启动令牌补充义务
privatevoidscheduleRefill(){
scheduler.scheduleAtFixedRate(()->{
synchronized(this){
tokens=Math.min(capacity,tokens+rate);//补充令牌,但不超越容量
},1,1,TimeUnit.SECONDS);//每秒发生一次性令牌
publicsynchronizedbooleantryAcquire(){
if(tokens>0){//判别令牌数量能否大于0
tokens--;//消耗一个令牌
returntrue;//失掉恳求成功
returnfalse;//令牌无余,无法失掉恳求
代码解读
capacity示意令牌桶的容量,即最大准许的恳求数量;rate示意令牌发生速率,即每秒发生的令牌数量。tokens示意以后令牌数量,scheduler是用于调度令牌补充义务的线程池。
在结构方法中,初始化令牌桶的容量和以后令牌数量,并启动令牌补充义务scheduleRefill()。
scheduleRefill()方法经常使用调度器活期执行令牌补充义务,每秒补充一次性令牌。在补充义务中,经过加锁的形式升级令牌数量,确保线程安保。补充的令牌数量为以后令牌数量加上发生速率,但不超越令牌桶的容量。
tryAcquire()方法经常使用synchronized关键字来成功线程安保,在方法中启动以下操作:
判别令牌数量能否大于0。
的 RateLimiter 限流组件,就是基于令牌桶算法成功的。
实用场景
普通用于包全自身的系统,对调用者启动限流,包全自身的系统不被突发的流量打垮。假设自身的系统实践的处置才干强于性能的流量限度时,可以准许肯定水平的流量突发,使得实践的处置速率高于性能的速率,充沛应用系统资源。
优劣剖析
优势:
劣势:
5.滑动日志算法(比拟冷门)
简介: 滑动日志限速算法须要记载恳求的时期戳,通经常常使用有序汇合来存储,咱们可以在单个有序汇合中跟踪用户在一个时期段内一切的恳求。
原理:
滑动日志算法可以用于成功限流性能,即控制系统在单位时期内处置恳求的数量,以包全系统免受过载的影响。以下是滑动日志算法用于限流的原理:
经过滑动日志算法启动限流,可以成功对单位时期内的恳求启动准确控制。它基于实时统计的形式,能够灵活地顺应恳求流量的变动,并且在内存经常使用上比拟高效。同时,经过调整时期窗口的长度和阈值的设置,可以灵敏地控制限流的精度和灵敏度。
成功形式
importjava.util.LinkedList;
importjava.util.List;
publicclassSlidingLogRateLimiter{
privateintrequests;//恳求总数
privateList<Long>timestamps;//存储恳求的时期戳列表
privatelongwindowDuration;//窗口继续时期,单位:毫秒
privateintthreshold;//窗口内的恳求数阀值
publicSlidingLogRateLimiter(intthreshold,longwindowDuration){
this.requests=0;
this.timestamps=newLinkedList<>();
this.windowDuration=windowDuration;
this.threshold=threshold;
publicsynchronizedbooleantryAcquire(){
longcurrentTime=System.currentTimeMillis();//失掉以后时期戳
//删除超越窗口继续时期的时期戳
while(!timestamps.isEmpty()&¤tTime-timestamps.get(0)>windowDuration){
timestamps.remove(0);
requests--;
if(requests<threshold){//判别以后窗口内恳求数能否小于阀值
timestamps.add(currentTime);//将以后时期戳参与到列表
requests++;//恳求总数参与
returntrue;//失掉恳求成功
returnfalse;//超越阀值,无法失掉恳求
代码解读
在以上代码中,requests示意恳求总数,timestamps用于存储恳求的时期戳列表,windowDuration示意窗口继续时期,threshold示意窗口内的恳求数阀值。
在结构函数中传入窗口内的恳求数阀值和窗口继续时期。
tryAcquire()方法经常使用了synchronized关键字来成功线程安保,在方法中启动以下操作:
经常使用这个滑动日志限流算法,可以限度在肯定时期窗口内的恳求频率,超越阀值的恳求会被限度。您可以依据实践需求和业务场景启动调整和经常使用。
实用场景: 对 实时性要求 高,且须要准确控制恳求速率的初级限流场景。
优劣剖析
优势:
劣势:
6.几种算法小结
7.罕用工具
1)RateLimiter(单机)
简介: 基于 令牌桶算法 成功的一个多线程限流器,它可以将恳求平均的启动处置,当然他并不是一个散布式限流器,只是对单机启动限流。它可以运行在定时拉取接口数。经过aop、filter、Interceptor等都可以到达限流成果。
用法
以下是一个基本的RateLimiter用法示例:
importcom..common.util.concurrent.RateLimiter;
publicclassRateLimiterDemo{
publicstaticvoidmain(String[]args){
//创立一个每秒准许2个恳求的RateLimiter
RateLimiterrateLimiter=RateLimiter.create(2.0);
while(true){
//恳求RateLimiter一个令牌
rateLimiter.acquire();
//执行操作
doSomeLimitedOperation();
privatestaticvoiddoSomeLimitedOperation(){
//模拟一些操作
System.out.println("Operationexecutedat:"+System.currentTimeMillis());
在这个例子中,RateLimiter.create(2.0)创立了一个每秒钟只准许2个操作的限速器。rateLimiter.acquire()方法会阻塞以后线程直到失掉到容许,确保调用doSomeLimitedOperation()操作的频率不会超越限度。
RateLimiter还提供了其余的方法,例如tryAcquire(),它会尝试失掉容许而不会阻塞,立刻前往失掉成功或失败的结果。还可以设置期待时期下限,比如tryAcquire(longtimeout,TimeUnitunit)可以设置最大期待时期。
Guava的RateLimiter十分灵敏,它支持平滑突发限度(SmoothBursty)友好滑预热限度(SmoothWarmingUp)等多种形式,可以依据特定的运行场景来选用适宜的限流战略。
2)sentinel(单机或许散布式)
简介: Sentinel是阿里巴巴开源的一款面向散布式系统的流量控制和熔断升级组件。它提供了实时的流量控制、熔断升级、系统负载包全和实时监控等性能,可以协助开发者包全系统的稳固性和牢靠性。
单机形式
集群形式
Sentinel集群限流服务端有两种启动形式:
用法
Sentinel的用法关键包含以下几个方面:
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
@SentinelResource(value="demo",blockHandler="handleBlock")
publicStringdemo(){
publicstaticvoidmain(String[]args){
System.setProperty("csp.sentinel.dashboard.server","localhost:8080");//设置控制台地址
System.setProperty("project.name","your-project-name");//设置运行称号
com.alibaba.csp.sentinel.init.InitExecutor.doInit();
SpringApplication.run(YourApplication.class,args);
3)Nginx(散布式)
简介: Nginx从网关这一层面思考,可以作为最前置的网关,抵御大局部的网络流量,因此经常使用Nginx启动限流也是一个很好的选用,在Nginx中,也提供了罕用的基于限流相关的战略性能。
用法
Nginx提供了两种限流方法:一种是控制速率,另一种是控制并发衔接数。
咱们须要经常使用limit_req_zone用来限度单位时期内的恳求数,即速率限度,
由于Nginx的限流统计是基于毫秒的,咱们设置的速度是2r/s,转换一下就是500毫秒内单个IP只准许经过1个恳求,从501ms开局才准许经过第2个恳求。
上方的速率控制只管很精准然而在消费环境难免太厚道了,实践状况下咱们应该控制一个IP单位总时期内的总访问次数,而不是像上方那样准确到毫秒,咱们可以经常使用burst关键字开启此设置。
burst=4意思是每个IP最多准许4个突发恳求
应用limit_conn_zone和limit_conn两个指令即可控制并发数
其中limit_connperip10示意限度单个IP同时最多能持有10个衔接;limit_connperserver100示意server同时能处置并发衔接的总数为100个。
留意:只要当requestheader被后端处置后,这个衔接才启动计数。
五、升级
1.简介
升级是在高并发或意外状况下舍弃非关键业务或简化处置的一种技术手腕。
按类型可分为有感升级,无感升级。
2.原理
在限流中,服务调用方为每一个调用的服务保养一个有限形态机,在这个形态时机有三种形态:封锁(调用远程服务)、半关上(尝试调用远程服务)和关上(前往失误)。这三种形态之间切换的环节如下:
当调用失败的次数累积到肯定的阈值时,熔断机制从封锁态切换到关上态。普通在成功时,假设调用成功一次性,就会重置调用失败次数。
当熔断处于关上形态时,咱们会启动一个计时器,当计时器超时后,形态切换到半关上态。也可以经过设置一个定时器,活期的探测服务能否复原。
当熔断处于半关上形态时,恳求可以到达后端服务,假设累计肯定的成功次数后,形态切换到封锁态;假设出现调用失败的状况,则切换到关上态。
3.罕用工具
4.其余
1)熔断
简介: 熔断在程序中,示意断开的意思。如出现了某事情,程序为了全体的稳固性,所以暂时(断开)中止服务一段时期,以保证程序可用时再被经常使用。
熔断和升级的区别
熔断程序为了全体的稳固性,所以暂时(断开)中止服务一段时期;升级(Degradation)降低级别的意思,它是指程序在出现疑问时,仍能保证有限性能可用的一种机制;
不同框架的熔断和升级的触发条件是不同,以Hystrix为例:
自动状况hystrix假设检测到10秒内恳求的失败率超越50%,就触发熔断机制。之后每隔5秒从新尝试恳求微服务,假设微服务不能照应,继续走熔断机制。假设微服务可达,则封锁熔断机制,复原反常恳求。
自动状况下,hystrix在以下4种条件下都会触发升级机制:
熔断时或许会调用升级机制,而升级时通常不会调用熔断机制。由于熔断是从全局登程,为了保证系统稳固性而停用服务,而升级是退而求其次,提供一种保底的处置打算,所以它们的归属相关是不同(熔断>升级)。
小结
作者丨骆天
高性能高并发网站架构,教你搭建Redis5缓存集群
一、Redis集群介绍 Redis真的是一个优秀的技术,它是一种key-value形式的NoSQL内存数据库,由ANSI C编写,遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 Redis最大的特性是它会将所有数据都放在内存中,所以读写速度性能非常好。 Redis是基于内存进行操作的,性能较高,可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动。 网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,需要快速响应,前端发送请求、后端和mysql数据库交互,进行sql查询操作,读写比较慢,这时候引入Redis ,把从mysql 的数据缓存到Redis 中,下次读取时候性能就会提高;当然,它也支持将内存中的数据以快照和日志的形式持久化到硬盘,这样即使在断电、机器故障等异常情况发生时数据也不会丢失,Redis能从硬盘中恢复快照数据到内存中。 Redis 发布了稳定版本的 5.0 版本,放弃 Ruby的集群方式,改用 C语言编写的 redis-cli的方式,是集群的构建方式复杂度大大降低。 Redis-Cluster集群采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。 redis-cluster投票:容错,投票过程是集群中所有master参与,如果半数以上master节点与master节点通信超过(cluster-node-timeout),认为当前master节点挂掉。 集群中至少应该有奇数个节点,所以至少有三个节点,每个节点至少有一个备份节点,所以下面使用6节点(主节点、备份节点由redis-cluster集群确定)。 6个节点分布在一台机器上,采用三主三从的模式。 实际应用中,最好用多台机器,比如说6个节点分布到3台机器上,redis在建立集群时为自动的将主从节点进行不同机器的分配。 二、单机redis模式 下载源码redis5.0并解压编译 wgettar xzf cd redis-5.0.0 make redis前端启动需要改成后台启动. 修改文件,将daemonize no -> daemonize yes vim 启动redis /www/server/redis/src/redis-server /www/server/redis/ 查看redis是否在运行 ps aux|grep redis现在是单机redis模式完成。 三、redis集群模式: 1.创建6个Redis配置文件 cd /usr/local/ mkdir redis_cluster //创建集群目录 cd redis_cluster mkdir 7000 7001 7002 7003 7004 7005//分别代表6个节点 其对应端口 7000 7001 7005 2.复制配置文件到各个目录 cp /www/server/redis/ /usr/local/redis_cluster/7000/ cp /www/server/redis/ /usr/local/redis_cluster/7001/ cp /www/server/redis/ /usr/local/redis_cluster/7002/ cp /www/server/redis/ /usr/local/redis_cluster/7003/ cp /www/server/redis/ /usr/local/redis_cluster/7004/ cp /www/server/redis/ /usr/local/redis_cluster/7005/ 3.分别修改配置文件 vim /usr/local/redis_cluster/7000/ vim /usr/local/redis_cluster/7001/ vim /usr/local/redis_cluster/7002/ vim /usr/local/redis_cluster/7003/ vim /usr/local/redis_cluster/7004/ vim /usr/local/redis_cluster/7005/ 如下 port 7000 #端口 cluster-enabled yes #启用集群模式 cluster-config-file nodes_ #集群的配置 配置文件首次启动自动生成 cluster-node-timeout 5000 #超时时间 5秒 appendonly yes #aof日志开启 它会每次写操作都记录一条日志 daemonize yes #后台运行 protected-mode no #非保护模式 pidfile /var/run/redis_ //下面可以不写 #若设置密码,master和slave需同时配置下面两个参数: masterauth jijiji #连接master的密码 requirepass jijiji #自己的密码 cluster-config-file,port,pidfile对应数字 4.启动节点 cd /www/server/redis/src/ ./redis-server /usr/local/redis_cluster/7000/ ./redis-server /usr/local/redis_cluster/7001/ ./redis-server /usr/local/redis_cluster/7002/ ./redis-server /usr/local/redis_cluster/7003/ ./redis-server /usr/local/redis_cluster/7004/ ./redis-server /usr/local/redis_cluster/7005/ 查看redis运行 ps aux|grep redis5.启动集群 /www/server/redis/src/redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1 这里使用的命令是create,因为我们要创建一个新的集群。 该选项--cluster-replicas 1意味着我们希望每个创建的主服务器都有一个从服。 输入yes至此,Reids5 集群搭建完成。 6.检查Reids5集群状态 可以执行redis-cli --cluster check host:port检查集群状态slots详细分配。 redis-cli --cluster info 127.0.0.1.停止Reids5集群 (1).因为Redis可以妥善处理SIGTERM信号,所以直接kill -9也是可以的,可以同时kill多个,然后再依次启动。 kill -9 PID PID PID (2)5 提供了关闭集群的工具,修改文件: /www/server/redis/utils/create-cluster/create-cluster端口PROT设置为6999,NODES为6,工具会生成 7000-7005 六个节点 用于操作。 修改后,执行如下命令关闭集群: /www/server/redis/utils/create-cluster/create-cluster stop 重新启动集群: /www/server/redis/utils/create-cluster/create-cluster start 8.帮助信息 执行redis-cli --cluster help,查看更多帮助信息 redis-cli --cluster help 吉海波
华为技术架构师分享:高并发场景下缓存处理的一些思路
在实际的开发当中,我们经常需要进行磁盘数据的读取和搜索,因此经常会有出现从数据库读取数据的场景出现。但是当数据访问量次数增大的时候,过多的磁盘读取可能会最终成为整个系统的性能瓶颈,甚至是压垮整个数据库,导致系统卡死等严重问题。
常规的应用系统中,我们通常会在需要的时候对数据库进行查找,因此系统的大致结构如下所示:
1.缓存和数据库之间数据一致性问题
常用于缓存处理的机制我总结为了以下几种:
首先来简单说说Cache aside的这种方式:
Cache Aside模式
这种模式处理缓存通常都是先从数据库缓存查询,如果缓存没有命中则从数据库中进行查找。
这里面会发生的三种情况如下:
缓存命中:
当查询的时候发现缓存存在,那么直接从缓存中提取。
缓存失效:
当缓存没有数据的时候,则从database里面读取源数据,再加入到cache里面去。
缓存更新:
当有新的写操作去修改database里面的数据时,需要在写操作完成之后,让cache里面对应的数据失效。
关于这种模式下依然会存在缺陷。比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。
Facebook的大牛们也曾经就缓存处理这个问题发表过相关的论文,链接如下:
分布式环境中要想完全的保证数据一致性是一件极为困难的事情,我们只能够尽可能的减低这种数据不一致性问题产生的情况。
Read Through模式
Read Through模式是指应用程序始终从缓存中请求数据。 如果缓存没有数据,则它负责使用底层提供程序插件从数据库中检索数据。 检索数据后,缓存会自行更新并将数据返回给调用应用程序。使用Read Through 有一个好处。
我们总是使用key从缓存中检索数据, 调用的应用程序不知道数据库, 由存储方来负责自己的缓存处理,这使代码更具可读性, 代码更清晰。但是这也有相应的缺陷,开发人员需要给编写相关的程序插件,增加了开发的难度性。
Write Through模式
Write Through模式和Read Through模式类似,当数据发生更新的时候,先去Cache里面进行更新,如果命中了,则先更新缓存再由Cache方来更新database。如果没有命中的话,就直接更新Cache里面的数据。
2.缓存穿透问题
在高并发的场景中,缓存穿透是一个经常都会遇到的问题。
什么是缓存穿透?
大量的请求在缓存中没有查询到指定的数据,因此需要从数据库中进行查询,造成缓存穿透。
会造成什么后果?
大量的请求短时间内涌入到database中进行查询会增加database的压力,最终导致database无法承载客户单请求的压力,出现宕机卡死等现象。
常用的解决方案通常有以下几类:
1.空值缓存
在某些特定的业务场景中,对于数据的查询可能会是空的,没有实际的存在,并且这类数据信息在短时间进行多次的反复查询也不会有变化,那么整个过程中,多次的请求数据库操作会显得有些多余。
不妨可以将这些空值(没有查询结果的数据)对应的key存储在缓存中,那么第二次查找的时候就不需要再次请求到database那么麻烦,只需要通过内存查询即可。这样的做法能够大大减少对于database的访问压力。
2.布隆过滤器
通常对于database里面的数据的key值可以预先存储在布隆过滤器里面去,然后先在布隆过滤器里面进行过滤,如果发现布隆过滤器中没有的话,就再去redis里面进行查询,如果redis中也没有数据的话,再去database查询。这样可以避免不存在的数据信息也去往存储库中进行查询情况。
什么是缓存雪崩?
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
如何避免缓存雪崩问题?
1.使用加锁队列来应付这种问题。当有多个请求涌入的时候,当缓存失效的时候加入一把分布式锁,只允许抢锁成功的请求去库里面读取数据然后将其存入缓存中,再释放锁,让后续的读请求从缓存中取数据。但是这种做法有一定的弊端,过多的读请求线程堵塞,将机器内存占满,依然没有能够从根本上解决问题。
2.在并发场景发生前,先手动触发请求,将缓存都存储起来,以减少后期请求对database的第一次查询的压力。数据过期时间设置尽量分散开来,不要让数据出现同一时间段出现缓存过期的情况。
3.从缓存可用性的角度来思考,避免缓存出现单点故障的问题,可以结合使用 主从+哨兵的模式来搭建缓存架构,但是这种模式搭建的缓存架构有个弊端,就是无法进行缓存分片,存储缓存的数据量有限制,因此可以升级为Redis Cluster架构来进行优化处理。(需要结合企业实际的经济实力,毕竟Redis Cluster的搭建需要更多的机器)
本地缓存 + Hystrix限流&降级,避免MySQL被打死。
使用 Ehcache本地缓存的目的也是考虑在 Redis Cluster 完全不可用的时候,Ehcache本地缓存还能够支撑一阵。
使用 Hystrix进行限流 & 降级 ,比如一秒来了5000个请求,我们可以设置假设只能有一秒 2000个请求能通过这个组件,那么其他剩余的 3000 请求就会走限流逻辑。
然后去调用我们自己开发的降级组件(降级),比如设置的一些默认值呀之类的。以此来保护最后的 MySQL 不会被大量的请求给打死。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。