SpringCloud系列——限流、熔断、降级
前言
分布式环境下,服务直接相互调用,一个复杂的业务可能要调用多个服务,例如A -> B -> C -> D,当某个服务出现异常(调用超时、调用失败等)将导致整个流程阻塞崩溃,严重的整个系统都会崩掉,为了实现高可用,必要的保护机制必不可少
本文记录限流、熔断、降级的实现处理
限流
我们采用令牌桶限流法,并自己实现一个简单令牌桶限流
有个任务线程以恒定速率向令牌桶添加令牌
一个请求会消耗一个令牌,令牌桶里的令牌大于0,才会放行,反正不允许通过
/**
* 简单的令牌桶限流
*/
public class RateLimiter { /**
* 桶的大小
*/
private Integer limit; /**
* 桶当前的token
*/
private static Integer tokens = 0; /**
* 构造参数
*/
public RateLimiter(Integer limit, Integer speed){
//初始化桶的大小,且桶一开始是满的
this.limit = limit;
tokens = this.limit; //任务线程:每秒新增speed个令牌
new Thread(() ->{
while (true){
try {
Thread.sleep(1000L); int newTokens = tokens + speed;
if(newTokens > limit){
tokens = limit;
System.out.println("令牌桶满了!!!");
}else{
tokens = newTokens;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} /**
* 根据令牌数判断是否允许执行,需要加锁
*/
public synchronized boolean execute() {
if (tokens > 0) {
tokens = tokens - 1;
return true;
}
return false;
}
}
main简单测试
public static void main(String[] args) {
//令牌桶限流:峰值每秒可以处理10个请求,正常每秒可以处理3个请求
RateLimiter rateLimiter = new RateLimiter(10, 3); //模拟请求
while (true){
//在控制台输入一个值按回车,相对于发起一次请求
Scanner scanner = new Scanner(System.in);
scanner.next(); //令牌桶返回true或者false
if(rateLimiter.execute()){
System.out.println("允许访问");
}else{
System.err.println("禁止访问");
}
}
}
在SpringCloud分布式下实现限流,需要把令牌桶的维护放到一个公共的地方,比如Zuul路由,当然也可以同时针对具体的每个服务进行单独限流
另外,guava里有现成的基于令牌桶的限流实现,引入
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>26.0-jre</version>
</dependency>
具体用法这里就不阐述了
我们找出之前的springcloud项目,在zuul-server中的AccessFilter过滤器进行限流,其他的都不变,只需要做如下修改
PS:我这里为了方便测试,调小了令牌桶的大小,跟速率,正常情况下要服务器的承受能力来定
/**
* Zuul过滤器,实现了路由检查
*/
public class AccessFilter extends ZuulFilter {
//令牌桶限流:峰值每秒可以处理10个请求,正常每秒可以处理3个请求
//PS:我这里为了方便测试,调小了令牌桶的大小,跟速率,正常情况下按服务器的承受能力来定
private RateLimiter rateLimiter = new RateLimiter(2, 1); //业务不变,省略其他代码... /**
* 过滤器的具体逻辑
*/
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse(); //限流
if(!rateLimiter.execute()){
try {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(200); //直接写入浏览器
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.println("系统繁忙,请稍后在试!<br/>System busy, please try again later!");
writer.flush();return null;
} catch (Exception e) {
e.printStackTrace();
}
} //业务不变,省略其他代码..
}
}
按照我们设置的值,一秒能处理一个请求,峰值一秒能处理两个请求,下面疯狂刷新进行测试
熔断
yml配置开启Hystrix熔断功能,进行容错处理
feign:
hystrix:
enabled: true
设置Hystrix的time-out时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 #毫秒
#或者设置从不超时
#timeout:
# enabled: false
在使用Feign调用服务提供者时配置@FeignClient的 fallback,进行容错处理(服务提供者发生异常),如果需要获取到异常信息,则要配置fallbackFactory<T>
@FeignClient(name = "sso-server", path = "/",/*fallback = SsoFeign.SsoFeignFallback.class,*/fallbackFactory = SsoFeign.SsoFeignFallbackFactory.class)
/**
* 容错处理(服务提供者发生异常,将会进入这里)
*/
@Component
public class SsoFeignFallback implements SsoFeign { @Override
public Boolean hasKey(String key) {
System.out.println("调用sso-server失败,进行SsoFeignFallback.hasKey处理:return false;");
return false;
}
}
/**
* 只打印异常,容错处理仍交给 SsoFeignFallback
*/
@Component
public class SsoFeignFallbackFactory implements FallbackFactory<SsoFeign> {
private final SsoFeignFallback ssoFeignFallback; public SsoFeignFallbackFactory(SsoFeignFallback ssoFeignFallback) {
this.ssoFeignFallback = ssoFeignFallback;
} @Override
public SsoFeign create(Throwable cause) {
cause.printStackTrace();
return ssoFeignFallback;
}
}
FallbackFactory也可以这样写
/**
* 容错处理
*/
@Component
public class SsoFeignFallbackFactory implements FallbackFactory<SsoFeign> { @Override
public SsoFeign create(Throwable cause) {
//打印异常
cause.printStackTrace(); return new SsoFeign() {
@Override
public Boolean hasKey(String key) {
System.out.println("调用sso-server失败:return false;");
return false;
}
};
}
}
因为我们没有启动Redis,报错,但我们进行容错处理,所以还是返回了false
降级
当调用服务发送异常,容错处理的方式有多种,我们可以:
1、重连,比如服务进行了限流,本次连接被限制,重连一次或N次就可以得到数据
2、直接返回一个友好提示
3、降级调用备用服务、返回缓存的数据等
后记
降级也可以叫做“备胎计划”...
代码开源
代码已经开源、托管到我的GitHub、码云:
GitHub:https://github.com/huanzi-qch/springCloud
码云:https://gitee.com/huanzi-qch/springCloud
SpringCloud系列——限流、熔断、降级的更多相关文章
- springBoot整合Sentinel实现降级限流熔断
由于hystrix的停止更新,以及阿里Sentinel在历年双十一的贡献.项目中使用了Sentinel,今天我们来讲讲Sentinel的入门教程,本文使用1.6.3版本进行讲解 本文通过Sentine ...
- Hystrix介绍以及服务的降级限流熔断
(dubbo熔断,Hystrix问的少) 无论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源.作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部 hang (挂起)在这个资源上, ...
- 高可用服务设计之二:Rate limiting 限流与降级
<高可用服务设计之二:Rate limiting 限流与降级> <nginx限制请求之一:(ngx_http_limit_conn_module)模块> <nginx限制 ...
- .net core使用ocelot---第四篇 限流熔断
简介 .net core使用ocelot---第一篇 简单使用 .net core使用ocelot---第二篇 身份验证 .net core使用ocelot---第三篇 日志记录 前几篇文章我们陆续介 ...
- 0.9.0.RELEASE版本的spring cloud alibaba sentinel限流、降级处理实例
先看服务提供方的,我们在原来的sentinel实例(参见0.9.0.RELEASE版本的spring cloud alibaba sentinel实例)上加上限流.降级处理,三板斧只需在最后那一斧co ...
- .Net微服务实践(四)[网关]:Ocelot限流熔断、缓存以及负载均衡
目录 限流 熔断 缓存 Header转化 HTTP方法转换 负载均衡 注入/重写中间件 后台管理 最后 在上篇.Net微服务实践(三)[网关]:Ocelot配置路由和请求聚合中我们介绍了Ocelot的 ...
- DBPack 限流熔断功能发布说明
上周我们发布了 v0.4.0 版本,增加了限流熔断功能,现对这两个功能做如下说明. 限流 DBPack 限流熔断功能通过 filter 实现.要设置限流规则,首先要定义 RateLimitFilter ...
- Dubbo学习系列之十(Sentinel之限流与降级)
各位看官,先提个问题,如果让你设计一套秒杀系统,核心要点是啥???我认为有三点:缓存.限流和分离.想当年12306大面积崩溃,还有如今的微博整体宕机情况,感觉就是限流降级没做好,"用有限的资 ...
- SpringCloud微服务:Sentinel哨兵组件,管理服务限流和降级
源码地址:GitHub·点这里||GitEE·点这里 一.基本简介 1.概念描述 Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性.包括核心的独立类库,监 ...
随机推荐
- Emgu-WPF学习使用 - 颜色映射
原文:Emgu-WPF学习使用 - 颜色映射 string sFile = ""; if (!String.IsNullOrEmpty(AppConstUtils.GDefault ...
- 【全面解禁!真正的Expression Blend实战开发技巧】序章
原文:[全面解禁!真正的Expression Blend实战开发技巧]序章 从silverlight2开始我也和大家一样一直在跟随微软的脚步,从sl2~sl4一步一步过来,总结了不少心得体会.由于各种 ...
- 【Python】Camera拍照休眠唤醒测试
#!/usr/bin/python # -*- coding: UTF-8 -*- import os import sys import time rebootCount = int(input(& ...
- Win8Metro(C#)数字图像处理--2.32图像曝光算法
原文:Win8Metro(C#)数字图像处理--2.32图像曝光算法 [函数名称] 图像曝光函数ExposureProcess(WriteableBitmap src,int exposureV ...
- VS2015设置VS2017的“快速操作”快捷键Alt+Enter
选项 - 环境 - 键盘 - 视图.快速操作和重构 添加“Alt+Enter (文本编辑器)”
- Cannot read property 'apply' of undefined
...TypeError: Cannot read property 'apply' of undefined :一般都是作用域不对 ...TypeError: Cannot read propert ...
- Newtonsoft.Json高级用法之枚举中文转义
最近看博客园中 焰尾迭的两篇关于"Newtonsoft.Json高级用法"的文章受到了很多人的评论,一度登入到头条推荐. 今天我就不再重复焰尾迭博文中的一些提过的Newtonsof ...
- C++数组指针、指针数组、函数指针的核心概念
1.什么叫数组指针? 数组指针:一个指向一维或者多维数组的指针. 比如:int * b=new int[10];指向一维数组的指针b ; 注意,这个时候释放空间一定要delete [] ,否则会造成内 ...
- 开源玩家福利:十大Linux免费游戏
假如当你考虑从Windows平台迁移至Linux平台时,“我能在Linux平台上游戏吗?”这类疑问正困扰着你,那么对此这有一个答案就是“快去Linux平台吧!”.感谢开源组织一直以来坚持不懈为Linu ...
- Delphi使用TObject类对象创建接受window消息(使用Classes.AllocateHWnd为对象创建一个尺寸为0的窗口,从而有了Handle)good
在delphi中,有时候我们希望对象可以接收windows消息,怎么办呢?因为要接收windows消息起码要有windows Handle,难道要建立的一个可见窗口?那样似乎太差强人意了.delphi ...