服务熔断
在微服务架构中, 根据业务来拆分成一个个的服务, 服务与服务之间可以通过RPC或者rest进行通信.
为了保证其高可用, 单个服务通常会集群部署, 由于网络原因或者自身的原因, 服务并不能保证100%可用.
如果单个服务出现问题, 调用这个服务就会出现线程阻塞, 此时若有大量的请求涌入, Servlet容器的线程资源会被消耗完毕, 引起雪崩, 导致服务瘫痪.
流量防卫兵Sentinel:
由阿里巴巴开源, 实现了熔断器模式, SpringCloud对这一组件进行了整合.
Sentinel 以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性.
- Sentinel 提供实时的监控功能
- 开箱即用, 快速整合其他开源框架
- 提供扩展api, 方便订制化
Feign整合Sentinel
一. 测试UserInfoConsumer1调用失败
-
正常传参调用
访问userInfoConsumer1的接口http://127.0.0.1:8091/testFun1?param=1
响应报文在以下两者间切换
{"result":"调用了userInfoProvider1, 方法: fun1"}
{"result":"调用了userInfoProvider2, 方法: fun1"}
-
传参制造未捕获的异常
访问userInfoConsumer1的接口http://127.0.0.1:8091/testFun1?param=0
发生500错误
二. 服务提供者和服务消费者模块整合Sentinel
-
编辑pom文件, 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
-
编辑配置文件, 添加配置
# 开启feign的sentinel支持, 默认为关闭状态
feign:
sentinel:
enabled: true
-
重启服务消费者和两个服务提供者, 正常传参调用
访问userInfoConsumer1的接口http://127.0.0.1:8091/testFun1?param=1
响应报文在以下两者间切换
{"result":"调用了userInfoProvider1, 方法: fun1"}
{"result":"调用了userInfoProvider2, 方法: fun1"}
-
传参制造未捕获的异常
访问userInfoConsumer1的接口http://127.0.0.1:8091/testFun1?param=0
响应报文为服务消费者中编写的容错报文{"result":"UserInfo Fall Back"}
表名本次异常调用被熔断, 不会造成级联阻塞.
Sentinel控制台
提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控、集群资源汇总以及规则管理的功能, 只需要对应用进行简单的配置, 就可以使用这些功能.
-
Sentinel DashBoard
# 下载源码
git clone https://github.com/alibaba/Sentinel.git
# 编译打包
mvn clean package -DskipTests
# 启动
java -jar sentinel-dashboard.jar
访问SentinelDashBoard首页http://127.0.0.1:8080
默认账号密码为sentinel/sentinel
-
实时监控
多次发起调用, 在Sentinel DashBoard控制台对应的服务中点击实时监控, 可以看到当前被调用的接口的实时QPS, 请求成功率以及响应时间等
-
流控规则
选择userInfoConsumer1服务 --> 簇点链路 --> /testFun1 --> 流控
阀值类型选择QPS, 单机阀值设置为2
保存后慢慢调用userInfoConsumer1的testFun1接口
http://127.0.0.1:8091/testFun1?param=1
响应报文在两个服务提供者之间轮询
{"result":"调用了userInfoProvider1, 方法: fun1"}
{"result":"调用了userInfoProvider2, 方法: fun1"}
加快调用速度, 当userInfoConsumer1的testFun1接口调用频率超过每秒2次之后, 响应报文开始出现
Blocked by Sentinel (flow limiting)
表示此方法已经被限流
-
降级规则
修改userInfoProvider1和userInfoProvider2的controller代码, 新增方法fun2, 以为userInfoProvider1为例
package cn.zack.controller;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 支持rest调用
*/
@RestController
public class UserInfoProviderController {
@GetMapping(path = "fun1")
public JSONObject fun1(@RequestParam Integer param) {
int a = 1 / param;
JSONObject jsonObject = new JSONObject();
jsonObject.put("result", "调用了userInfoProvider1, 方法: fun1");
return jsonObject;
}
@GetMapping(path = "fun2")
public JSONObject fun2(@RequestParam Integer param) {
int a = 1 / param;
JSONObject jsonObject = new JSONObject();
jsonObject.put("result", "调用了userInfoProvider1, 方法: fun2");
return jsonObject;
}
}
重启服务消费者和两个服务提供者, 以userInfoProvider2的fun2方法为例:
正常调用fun1方法http://127.0.0.1:8082/fun1?param=1
得到响应:
{"result":"调用了userInfoProvider2, 方法: fun1"}
正常调用fun2方法http://127.0.0.1:8082/fun2?param=1
得到响应:
{"result":"调用了userInfoProvider2, 方法: fun2"}
模拟制造异常调用fun1方法http://127.0.0.1:8082/fun1?param=0
响应状态码500
模拟制造异常调用fun2方法http://127.0.0.1:8082/fun2?param=0
响应状态码500
在Sentinel控制台选择userInfoProvider2的fun2方法, 设置为异常比例超过10%进行降级:
再次模拟制造异常调用fun2方法http://127.0.0.1:8082/fun2?param=0
重复多次调用后不再响应状态码500, 而是响应Blocked by Sentinel (flow limiting)
此时正常调用userInfoProvider2的fun2方法, 也得到响应Blocked by Sentinel (flow limiting)
此时正常调用userInfoProvider2的fun1方法, 可以正常得到响应
此时模拟制造异常调用userInfoProvider2的fun1方法, 返回状态码500
说明此userInfoProvider2服务的fun2接口已被降级, 不再允许被调用, 而此服务的其他接口不受影响.
-
热点规则
// todo
-
授权规则
// todo
-
系统规则
//todo
-
集群流控
// todo