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控制台查看流控规则,限流规则依然存在,说明持久化成功了

相关推荐
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-1 天前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫1 天前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly1 天前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh1 天前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode1 天前
Spring 依赖注入方式全景解析
java·后端·spring