SpringCloudAlibaba技术整理(三)服务熔断-sentinel

服务熔断

在微服务架构中, 根据业务来拆分成一个个的服务, 服务与服务之间可以通过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
image.png

  • 实时监控

多次发起调用, 在Sentinel DashBoard控制台对应的服务中点击实时监控, 可以看到当前被调用的接口的实时QPS, 请求成功率以及响应时间等
image.png

  • 流控规则

选择userInfoConsumer1服务 --> 簇点链路 --> /testFun1 --> 流控
image.png
阀值类型选择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%进行降级:
无标题.png

再次模拟制造异常调用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