springcloud alibaba(六)Sentinel 配置

文章目录

Sentinel 中实现自定义异常返回的核心机制

在 Sentinel 中实现自定义异常返回的核心机制是:通过定制异常处理器,当触发限流、熔断等规则时,系统会返回符合业务需求的响应格式,而非默认的提示信息。

核心概念

Sentinel 的异常返回定制主要依赖两个核心接口:

  1. BlockExceptionHandler:处理 Sentinel 定义的限流 / 熔断等阻塞异常(BlockException),这是自定义返回的核心接口。
  2. UrlBlockHandler(已过时):旧版本接口,新版本推荐使用BlockExceptionHandler。

Sentinel 默认的异常返回是一段简单的文本(如Blocked by Sentinel (flow limiting)),通过实现BlockExceptionHandler,可以自定义返回 JSON 格式、指定 HTTP 状态码等。

Feign 整合 Sentinel
  1. 在pom.xml文件中引入 Sentinel 依赖;
xml 复制代码
<!--sentinel客户端-->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

如果已经引入Sentinel, 可忽略.

  1. 在配置文件中开启Feign对Sentinel的支持
yaml 复制代码
feign:
	sentinel:
		enabled: true
自定义异常处理器
  1. 创建配置类,实现BlockExceptionHandler接口:
java 复制代码
package com.goblin.feign.fallback;

import com.goblin.entity.Product;
import com.goblin.feign.ProductClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 这是一个容错类, 直接实现了微服务接口。在使用的时候,需要在 FeignClient接口中,使用
 * fallback 进行引用
 */
@Component
@Slf4j
public class productClientFallback implements ProductClient {
    /**
     *
     * @param pid
     * @return
     */
    @Override
    public Product findById(Long pid) {
        Product product = new Product();
        product.setPid(-100L);
        product.setPname("商品微服务调用出现异常了,已经进入到了容错方法中");
        return product;
    }
}
  1. 在Feign调用客户端配置异常类
java 复制代码
package com.goblin.feign;

import com.goblin.entity.Product;
import com.goblin.feign.fallback.ProductClientFallbackFactory;
import com.goblin.feign.fallback.productClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 调用产品微服务
 */
@FeignClient(name = "service-product"/*, path = "/product"*/
        ,fallback = productClientFallback.class //这种形式不会获取到具体的错误信息
)
public interface ProductClient {

    @GetMapping("/product/{pid}")
    Product findById(@PathVariable Long pid);
}

这种方式不能获取到异常信息, 还有另一个方式可以获取到异常信息;

自定义异常处理器,方式2
  1. 创建配置类,实现FallbackFactory接口:
java 复制代码
package com.goblin.feign.fallback;

import com.goblin.entity.Product;
import com.goblin.feign.ProductClient;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 这是一个容错类, 需要实现 FallbackFactory<要为哪个接口产生容错类>,可以拿到具体的错误信息
 * 在使用的时候,需要在 FeignClient接口中使用fallbackFactory进行引用
 */
@Slf4j
@Component
public class ProductClientFallbackFactory implements FallbackFactory<ProductClient> {
    @Override
    public ProductClient create(Throwable throwable) {
        return new ProductClient() {
            @Override
            public Product findById(Long pid) {
                throwable.printStackTrace();
                Product product = new Product();
                product.setPid(-1L);
                return product;
            }
        };
    }
}
  1. 在Feign调用客户端配置异常类
java 复制代码
package com.goblin.feign;

import com.goblin.entity.Product;
import com.goblin.feign.fallback.ProductClientFallbackFactory;
import com.goblin.feign.fallback.productClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 调用产品微服务
 */
@FeignClient(name = "service-product"/*, path = "/product"*/
        /*
         fallback 和 fallbackFactory 两个都是引用容错类的, 但是有区别,
            1> fallbackFactory 可以拿到容错类中的错误信息
            2> 以上两个只能使用一个, 不能同时使用
        */
        //,fallback = productClientFallback.class //这种形式不会获取到具体的错误信息
        ,fallbackFactory = ProductClientFallbackFactory.class // 可以在容错类中获取到具体的错误信息
)
public interface ProductClient {

    @GetMapping("/product/{pid}")
    Product findById(@PathVariable Long pid);
}
sentinel持久化

现在可以通过Dashboard来为每个Sentinel客户端设置各种各样的规则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。

本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更

新规则,也可以通过 Sentinel 控制台推送规则。

  1. 创建持久化的类,实现InitFunc接口
java 复制代码
package com.goblin.config;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.util.List;

//规则持久化
public class FilePersistence implements InitFunc {

    @Value("spring.application:name")
    private String appcationName;

    @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.在资源目录添加目并配置持久化类

持久化验证

shop-order 微服务重启服务, 在Sentinel控制台配置流控数据。重启微服务,在Sentinel控制台查看流控规则,限流规则依然存在,说明持久化成功了

相关推荐
不会玩电脑的Xin.2 小时前
Spring框架入门:IOC与AOP实战
java·后端·spring
摇滚侠2 小时前
2025最新 SpringCloud 教程,接口测试,本地事务,打通链路,笔记65,笔记66,笔记67
笔记·spring·spring cloud
iナナ3 小时前
Java自定义协议的发布订阅式消息队列(二)
java·开发语言·jvm·学习·spring·消息队列
雨中飘荡的记忆3 小时前
Spring WebFlux详解
java·后端·spring
Unstoppable223 小时前
八股训练营第 39 天 | Bean 的作用域?Bean 的生命周期?Spring 循环依赖是怎么解决的?Spring 中用到了那些设计模式?
java·spring·设计模式
Java天梯之路3 小时前
Spring AOP:面向切面编程的优雅解耦之道
java·spring·面试
血小溅3 小时前
Spring Cloud 整合 Nacos:注册中心初始化全解析与 Spring 扩展技术揭秘(新增 Spring Boot 版本迭代差异)
spring cloud
qq_348231853 小时前
Spring AI核心知识点
java·人工智能·spring
古城小栈4 小时前
Spring AI Alibaba 重磅更新:Java 的开发新纪元
java·人工智能·spring