SA实战 ·《SpringCloud Alibaba实战》第11章-服务容错加餐:Sentinel核心技术 下

简介: SA实战 ·《SpringCloud Alibaba实战》第11章-服务容错加餐:Sentinel核心技术

授权规则

授权规则能够根据调用来源判断还否允许执行本次请求。

授权规则概述

在某些场景下,需要根据调用接口的来源判断是否允许执行本次请求。此时就可以使用Sentinel提供的授权规则来实现,Sentinel的授权规则能够根据请求的来源判断是否允许本次请求通过。

在Sentinel的授权规则中,提供了 白名单与黑名单 两种授权类型。

演示授权规则

(1)在订单微服务shop-order中新建io.binghe.shop.order.parser包,并创建MyRequestOriginParser类,实现com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser接口,用来处理请求的来源。代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description Sentinel授权规则,用来处理请求的来源
 */
@Component
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        return httpServletRequest.getParameter("serverName");
    }
}

(2)首先在浏览器中访问

http://localhost:8080/order/request_sentinel4,在Sentinel的簇点链路里找到/request_sentinel4。

图片.pngimage.gif


(3)点击授权按钮,进入授权规则配置框,按照如下方式进行配置。

图片.pngimage.gif


其中,流控应用填写的是test,授权类型为黑名单。这里要结合新建的MyRequestOriginParser类进行理解,MyRequestOriginParser类的parseOrigin()方法如下所示。

public String parseOrigin(HttpServletRequest httpServletRequest) {
    return httpServletRequest.getParameter("serverName");
}

parseOrigin()方法中直接返回了从HttpServletRequest中获取的serverName参数,而在上图中的流控应用中输出的是test,授权类型为黑名单。

所以,如果我们访问http://localhost:8080/order/request_sentinel4?serverName=test的话,是处于黑名单的状态,无法访问。

(4)点击新增按钮后,不断在浏览器中刷新http://localhost:8080/order/request_sentinel4?serverName=test,会发现无法访问,被Sentinel限流了。

图片.pngimage.gif


系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、 RT、入口 QPS 、 CPU使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统规则概述

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量 (进入应用的流量) 生效。

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
  • CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护

演示系统规则

(1)在订单微服务中新建io.binghe.shop.order.handler包,并创建MyUrlBlockHandler类,实现com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler接口,用来捕获系统级Sentinel异常,代码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 处理Sentinel系统规则,返回自定义异常
 */
@Component
public class MyUrlBlockHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = null;
        if (e instanceof FlowException) {
            msg = "限流了";
        } else if (e instanceof DegradeException) {
            msg = "降级了";
        } else if (e instanceof ParamFlowException) {
            msg = "热点参数限流";
        } else if (e instanceof SystemBlockException) {
            msg = "系统规则(负载/...不满足要求)";
        } else if (e instanceof AuthorityException) {
            msg = "授权规则不通过";
        }
        // http状态码
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Type", "application/json;charset=utf-8");
        response.setContentType("application/json;charset=utf-8");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 500);
        jsonObject.put("codeMsg", msg);
        response.getWriter().write(jsonObject.toJSONString());
    }
}

(2)在订单微服务的io.binghe.shop.order.controller.SentinelController类中新增requestSentinel5()方法,如下所示。

@GetMapping(value = "/request_sentinel5")
@SentinelResource("request_sentinel5")
public String requestSentinel5(){
    log.info("测试Sentinel5");
    return "sentinel5";
}

(3)首先在浏览器中访问

http://localhost:8080/order/request_sentinel5,在Sentinel的簇点链路里找到/request_sentinel5。

图片.pngimage.gif


(4)点击流控按钮,进入流控规则配置框,按照如下方式进行配置。

图片.pngimage.gif


(5)在浏览器中不断刷新

http://localhost:8080/order/request_sentinel5,会显示如下信息。

图片.pngimage.gif


返回的原始数据如下所示。

{"code":500,"codeMsg":"限流了"}

说明触发了系统规则,捕获到了Sentinel全局异常。

@SentinelResource注解

使用Sentinel时,可以使用@SentinelResource注解来指定异常处理策略。

@SentinelResource注解概述

在Sentinel中,指定发生异常时的处理策略非常简单,只需要使用@SentinelResource注解即可,@SentinelResource注解的源码如下所示。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
 //资源名称
    String value() default "";
 //entry类型,标记流量的方向,取值IN/OUT,默认是OUT
    EntryType entryType() default EntryType.OUT;
    int resourceType() default 0;
 //处理BlockException的函数名称,函数要求:
    //1. 必须是 public
    //2.返回类型 参数与原方法一致
    //3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置
    //blockHandlerClass ,并指定blockHandlerClass里面的方法。
    String blockHandler() default "";
    //存放blockHandler的类,对应的处理函数必须static修饰。
    Class<?>[] blockHandlerClass() default {};
    //用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所
    //有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:
    //1. 返回类型与原方法一致
    //2. 参数类型需要和原方法相匹配
    //3. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定fallbackClass里面的方法。
    String fallback() default "";
    //存放fallback的类。对应的处理函数必须static修饰。
    String defaultFallback() default "";
 //用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进
    //行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:
    //1. 返回类型与原方法一致
    //2. 方法参数列表为空,或者有一个 Throwable 类型的参数。
    //3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置fallbackClass ,并指定 fallbackClass 里面的方法。
    Class<?>[] fallbackClass() default {};
   //指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
 //需要trace的异常
    Class<? extends Throwable>[] exceptionsToIgnore() default {};
}

演示@SentinelResource注解

1.定义限流和降级后的处理方法

(1)在订单微服务的io.binghe.shop.order.service.SentinelService接口中新增sendMessage2()方法,如下所示。

String sendMessage2();

(2)在订单微服务的io.binghe.shop.order.service.impl.SentinelServiceImpl方法中,实现sendMessage2()方法,并且定义一个成员变量count,用来记录请求sendMessage2()方法的次数,同时定义25%的异常率。在sendMessage2()方法上使用@SentinelResource指定了资源的名称、发生BlockException时进入的方法和发生异常时进入的方法,代码如下所示。

private int count = 0;
@Override
@SentinelResource(
    value = "sendMessage2",
    blockHandler = "blockHandler",
    fallback = "fallback")
public String sendMessage2() {
    count ++;
    //25%的异常率
    if (count % 4 == 0){
        throw new RuntimeException("25%的异常率");
    }
    return "sendMessage2";
}
public String blockHandler(BlockException e){
    log.error("限流了:{}", e);
    return "限流了";
}
public String fallback(Throwable e){
    log.error("异常了:{}", e);
    return "异常了";
}

(3)在订单微服务的io.binghe.shop.order.controller.SentinelController类中新增requestSentinel6()方法,在方法中调用io.binghe.shop.order.service.SentinelService接口中的sendMessage2()方法,如下所示。

@GetMapping(value = "/request_sentinel6")
public String requestSentinel6(){
    log.info("测试Sentinel6");
    return sentinelService.sendMessage2();
}

(4)首先在浏览器中访问

http://localhost:8080/order/request_sentinel6,在Sentinel的簇点链路里找到/request_sentinel6。

图片.pngimage.gif


(5)点击流控按钮进入流控规则页面,按照下图方式进行配置。

图片.pngimage.gif


(6)点击新增按钮后在浏览器中刷新http://localhost:8080/order/request_sentinel6,当刷新的频率超过每秒2次时,浏览器会显示如下信息。

图片.pngimage.gif


当刷新的次数是4的倍数时,浏览器会显示如下信息。

图片.pngimage.gif


2.在外部类中指定限流和异常调用的方法

(1)在订单微服务的io.binghe.shop.order.handler包下新建

MyBlockHandlerClass类,用于定义被Sentinel限流时的方法,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 定义被Sentinel限流时调用的方法
 */
@Slf4j
public class MyBlockHandlerClass {
    public static String blockHandler(BlockException e){
        log.error("限流了:{}", e);
        return "限流了";
    }
}

(2)在订单微服务的io.binghe.shop.order.handler包下新建MyFallbackClass类,用于定义抛出异常时调用的方法,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description 定义异常时调用的方法
 */
@Slf4j
public class MyFallbackClass {
    public static String fallback(Throwable e){
        log.error("异常了:{}", e);
        return "异常了";
    }
}

(3)修改io.binghe.shop.order.service.impl.SentinelServiceImpl#sendMessage2()方法上的注解,修改后的代码如下所示。

@Override
@SentinelResource(
    value = "sendMessage2",
    blockHandlerClass = MyBlockHandlerClass.class,
    blockHandler = "blockHandler",
    fallbackClass = MyFallbackClass.class,
    fallback = "fallback")
public String sendMessage2() {
    count ++;
    System.out.println(count);
    //25%的异常率
    if (count % 4 == 0){
        throw new RuntimeException("25%的异常率");
    }
    return "sendMessage2";
}

(4)首先在浏览器中访问

http://localhost:8080/order/request_sentinel6,在Sentinel的簇点链路里找到/request_sentinel6。

图片.png


(5)点击流控按钮进入流控规则页面,按照下图方式进行配置。

图片.png


(6)点击新增按钮后在浏览器中刷新http://localhost:8080/order/request_sentinel6,当刷新的频率超过每秒2次时,浏览器会显示如下信息。

图片.pngimage.gif


当刷新的次数是4的倍数时,浏览器会显示如下信息。

图片.pngimage.gif


Sentinel持久化

Sentinel中可以自定义配置的持久化来将Sentinel的配置规则持久化到服务器磁盘,使得重启应用或者Sentinel后,Sentinel的配置规则不丢失。

Sentinel持久化概述

细心的小伙伴会发现,我们之前配置的Sentinel规则在程序重启或者Sentinel重启后就会消失不见,此时就需要我们重新配置。如果这发生在高并发、大流量的场景下是不可接受的。那有没有什么办法让程序或Sentinel重启后配置不丢失呢?其实,Sentinel中可以自定义配置的持久化来解决这个问题。

实现Sentinel的持久化

(1)在订单微服务shop-order中新建io.binghe.shop.order.persistence包,并创建SentinelPersistenceRule类,实现com.alibaba.csp.sentinel.init.InitFunc接口,并在SentinelPersistenceRule类中获取应用的名称,覆写init()方法,源码如下所示。

/**
 * @author binghe
 * @version 1.0.0
 * @description Sentinel规则持久化
 */
public class SentinelPersistenceRule implements InitFunc {
    //实际可以从外部配置读取
    private String appcationName = "server-order";
    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + appcationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }
    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );
    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );
    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );
    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }
    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }
    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

(2)在订单微服务的resources目录下新建META-INF目录,并在META-INF目录下新建services目录,在services目录下新建名称为com.alibaba.csp.sentinel.init.InitFunc的文件,如下所示。

image.gif图片.png


(3)在com.alibaba.csp.sentinel.init.InitFunc文件中添加io.binghe.shop.order.persistence.SentinelPersistenceRule类的全类名,如下所示。

io.binghe.shop.order.persistence.SentinelPersistenceRule

图片.pngimage.gif


(4)首先在浏览器中访问

http://localhost:8080/order/request_sentinel6,在Sentinel的簇点链路里找到/request_sentinel6。

图片.pngimage.gif


(5)点击流控按钮进入流控规则页面,按照下图方式进行配置。

图片.pngimage.gif


(6)点击新增按钮,此时打开电脑的user.home目录,我电脑的目录为C:\Users\binghe,可以发现C:\Users\binghe目录中多了一个sentinel-rules目录。

image.gif图片.png


(7)打开sentinel-rules目录,发现里面存在一个server-order目录,如下所示。

image.gif图片.png


(8)打开server-order目录后,会发现生成了Sentinel的配置文件,并持久化到了磁盘上,如下所示。

图片.pngimage.gif


(9)打开flow-rule.json文件,内容如下所示。

[
    {
        "clusterConfig": {
            "acquireRefuseStrategy": 0,
            "clientOfflineTime": 2000,
            "fallbackToLocalWhenFail": true,
            "resourceTimeout": 2000,
            "resourceTimeoutStrategy": 0,
            "sampleCount": 10,
            "strategy": 0,
            "thresholdType": 0,
            "windowIntervalMs": 1000
        },
        "clusterMode": false,
        "controlBehavior": 0,
        "count": 2,
        "grade": 1,
        "limitApp": "default",
        "maxQueueingTimeMs": 500,
        "resource": "/request_sentinel6",
        "strategy": 0,
        "warmUpPeriodSec": 10
    }
]

可以看到,flow-rule.json文件中持久化了对于/request_sentinel6接口的配置。

至此,我们完成了Sentinel规则的持久化。

相关文章
|
13天前
|
监控 虚拟化 Docker
Sentinel监测到了服务,但是实时监控不显示曲线图,应该怎么解决这个问题?
在虚拟机(VMware,IP:192.168.175.100)中部署了Sentinel,使用Docker运行,端口映射为8858。服务的`application.properties`配置指向Sentinel仪表盘(192.168.175.100:8858),客户端IP为物理机IP(192.168.2.2)。然而,cart-service在Sentinel中显示失联且无曲线图,而sentinel-dashboard自身监控正常。
|
1天前
|
人工智能 SpringCloudAlibaba 自然语言处理
SpringCloud Alibaba AI整合DeepSeek落地AI项目实战
在现代软件开发领域,微服务架构因其灵活性、可扩展性和模块化特性而受到广泛欢迎。微服务架构通过将大型应用程序拆分为多个小型、独立的服务,每个服务运行在其独立的进程中,服务与服务间通过轻量级通信机制(通常是HTTP API)进行通信。这种架构模式有助于提升系统的可维护性、可扩展性和开发效率。
23 1
|
4月前
|
存储 数据可视化 Java
基于MicrometerTracing门面和Zipkin实现集成springcloud2023的服务追踪
Sleuth将会停止维护,Sleuth最新版本也只支持springboot2。作为替代可以使用MicrometerTracing在微服务中作为服务追踪的工具。
221 1
|
1月前
|
人工智能 安全 Java
AI 时代:从 Spring Cloud Alibaba 到 Spring AI Alibaba
本次分享由阿里云智能集团云原生微服务技术负责人李艳林主讲,主题为“AI时代:从Spring Cloud Alibaba到Spring AI Alibaba”。内容涵盖应用架构演进、AI agent框架发展趋势及Spring AI Alibaba的重磅发布。分享介绍了AI原生架构与传统架构的融合,强调了API优先、事件驱动和AI运维的重要性。同时,详细解析了Spring AI Alibaba的三层抽象设计,包括模型支持、工作流智能体编排及生产可用性构建能力,确保安全合规、高效部署与可观测性。最后,结合实际案例展示了如何利用私域数据优化AI应用,提升业务价值。
194 4
|
2月前
|
SpringCloudAlibaba 负载均衡 Dubbo
【SpringCloud Alibaba系列】Dubbo高级特性篇
本章我们介绍Dubbo的常用高级特性,包括序列化、地址缓存、超时与重试机制、多版本、负载均衡。集群容错、服务降级等。
【SpringCloud Alibaba系列】Dubbo高级特性篇
|
2月前
|
存储 SpringCloudAlibaba Java
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论。
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
|
2月前
|
SpringCloudAlibaba JavaScript Dubbo
【SpringCloud Alibaba系列】Dubbo dubbo-admin安装教程篇
本文介绍了 Dubbo-Admin 的安装和使用步骤。Dubbo-Admin 是一个前后端分离的项目,前端基于 Vue,后端基于 Spring Boot。安装前需确保开发环境(Windows 10)已安装 JDK、Maven 和 Node.js,并在 Linux CentOS 7 上部署 Zookeeper 作为注册中心。
【SpringCloud Alibaba系列】Dubbo dubbo-admin安装教程篇
|
2月前
|
SpringCloudAlibaba Dubbo Java
【SpringCloud Alibaba系列】Dubbo基础入门篇
Dubbo是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能。
【SpringCloud Alibaba系列】Dubbo基础入门篇
|
1月前
|
人工智能 自然语言处理 Java
Spring Cloud Alibaba AI 入门与实践
本文将介绍 Spring Cloud Alibaba AI 的基本概念、主要特性和功能,并演示如何完成一个在线聊天和在线画图的 AI 应用。
414 7
|
2月前
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
520 13
Spring Cloud Alibaba:一站式微服务解决方案