天天看点

SpringCloud Alibaba——Sentinel服务容错(学习记录)

雪崩效应

在下图的微服务中,当A挂掉之后,B还是一直在请求A,那么B发往A的请求就会强制等待直到超时。一个请求就是一个线程,当被等待时候就是线程阻塞,只有当超时之后才会释放;当高并发的情况下多个线程开启就对应了服务器的资源,如果不做处理B也会挂了,同理CD也是如此。

基础服务故障导致上层服务故障,故障不断放大的过程称为雪崩效应,也称为级联失效,级联故障(cascading failure)。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

常见容错方案

超时

给每个请求都设置一个比较短的超时时间,比如1秒,不管有没有请求成功,这个线程在1s之后就会被释放。

只要我速度够快,就不容易挂。

限流

如某个微服务最大的QPS是1000,那么就为他设置一个QPS最大值,如800,当有请求进来QPS超过了800,后续的请求就直接拒绝。

最大的量就是设置的值,再多也没用。

仓壁模式

比如两个A、B Controller,每个Controller都有自己的线程池,比如都是20,当线程池满了之后会排队,然后拒绝请求,那么A、B都是自己的线程池,并不会影响对方。

不把鸡蛋都放在同一个篮子里

断路器模式

断路器的思想就是可以为某个服务(接口)去设置一个错误率的阀值,当在一定时间内达到了这个阀值就跳闸(断开),不再去调用,也就是不能访问了。断路器断开之后,过一会会进入半开状态,这是一个瞬间太,他会再去调用服务,如果成功就会关闭断路器,反之继续打开。

监控-开关

SpringCloud Alibaba——Sentinel服务容错(学习记录)

使用Sentinel实现容错

面向云原生微服务的高可用流控防护组件

GitHub

官网

引入依赖

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-alibaba-sentinel</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
           

配置

management:
  endpoints:
    web:
      exposure:
        include: '*'
           
SpringCloud Alibaba——Sentinel服务容错(学习记录)

搭建Sentinel控制台

下载地址

根据自己项目中的sentinel版本进行对应下载

下载对应的jar之后就可以直接启动了,访问http://localhost:8080/

spring:
  cloud:
    sentinel:
      transport:
        # 指定sentinel控制台地址
        dashboard: localhost:8080
           

启动项目之后在sentinel是看不到东西的,就一个首页,当访问项目的接口之后就可以看到了,这是因为sentinel是懒加载的这是Ribbon一样。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

设置Sentinel流控规则

在族点链路中可以查看到访问过的API,可以为他进行流控设置

SpringCloud Alibaba——Sentinel服务容错(学习记录)

QPS:1,直接 就是访问一次之后就限流

SpringCloud Alibaba——Sentinel服务容错(学习记录)

关联: 当关联的资源达到阀值,就限流自己。

比如:关联了/actuator/sentinel,当/actuator/sentinel被限流,/shares/1就会被限流。

链路: 只记录指定链路上的流量

@Service
@Slf4j
public class TestService {
    @SentinelResource("common")
    public String common() {
        log.info("执行了Common");
        return "common";
    }
}
           
@Autowired
    private TestService testService;

    @GetMapping("test-a")
    public String testA() {
        this.testService.common();
        return "test-a";
    }

    @GetMapping("test-b")
    public String testB() {
        this.testService.common();
        return "test-b";
    }
           

为common添加流控

SpringCloud Alibaba——Sentinel服务容错(学习记录)

添加链路类型的规则

SpringCloud Alibaba——Sentinel服务容错(学习记录)

这时候访问一次/test-a接口之后就不会在拿到结果

也就是说对于common这个资源他是只统计了/test-a接口,可以说这是细粒度针对来源到API接口,而上面的这种针对来源是对于微服务的

SpringCloud Alibaba——Sentinel服务容错(学习记录)

快速失败: 直接抛异常

Warm Up: 预热。根据codeFactor(默认3)的值,从阀值/codeFactor,经过预热时长,才到达设置的QPS阀值。

比如:我给阀值100,预热10秒,那么就是100/3作为最初的阈值,经过10秒之后才去达到100限流。

适用场景:适合秒杀开始,当秒杀开始后的时候可以让经过的流量缓慢的增加,不能直接一下子这么多,可能会挂掉。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

排队等待: 匀速排队,让请求以均匀的速度通过,阀值类型必须设置QPS,否则无效。

以下的设置:每秒只允许一次请求,其他的在后面排队,直到过了超时时间把其他的请求丢弃。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

适用场景:应对突发流量的请求,有时候流量多,有时候又空闲,如果希望机器能在空闲的时候处理请求,而不是直接拒绝。

设置Sentinel降级规则

SpringCloud Alibaba——Sentinel服务容错(学习记录)

RT

平均响应时间(秒级统计)超出阀值(1)并且在窗口内通过的请求>=5就会触发降级(打开断路器),等时间窗口结束之后,再关闭降级。

SpringCloud Alibaba——Sentinel服务容错(学习记录)
SpringCloud Alibaba——Sentinel服务容错(学习记录)
注意点

RT默认最大是4900ms,不管给到多大就只能是4900ms ,如果要进行修改最大是可以修改属性-Dcsp.sentinel.statistic.max.rt=xxx

异常比例

QPS>=5 并且异常比例(秒级统计)超过阀值就会触发降级(断路器打开),等待时间窗口结束,再次关闭降级。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

异常数

异常数(分钟统计)超过阀值就会触发降级(断路器打开),等待时间窗口结束,再次关闭降级。

SpringCloud Alibaba——Sentinel服务容错(学习记录)
注意点

时间窗口<60秒回出现问题

如果一分钟内统计的异常数大于10就开降级,10秒之后恢复。异常数是分钟级别的,那么可能时间窗口结束的10秒还是在同一分钟里,所以还是会处于降级状态。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

热点规则

@GetMapping("test-hot")
    @SentinelResource("hot")
    public String testHot(
        @RequestParam(required = false) String a,
        @RequestParam(required = false) String b
    ) {
        return a + " " + b;
    }
           

参数索引0就是代表的接口中的第一个参数,1就是代表的第二个参数,单机阀值(QPS)

在时间窗口内,参数索引的QPS达到了阈值就会限流

SpringCloud Alibaba——Sentinel服务容错(学习记录)
SpringCloud Alibaba——Sentinel服务容错(学习记录)

意思就是当第一个参数值为5的时候,阈值为1000,否则就是1。热点规则就是一个特殊的流控规则。

SpringCloud Alibaba——Sentinel服务容错(学习记录)

适用场景:某个参数的QPS特别搞,而又不影响整个API对于其他参数的调用。

注意点

参数必须是基本类型或者String

系统规则

SpringCloud Alibaba——Sentinel服务容错(学习记录)

Load

当系统load1(1分钟的load)超过阈值,且并发线程数超过系统容量时触发,建议设置为CPU核心数*2.5.(仅对Linux和Unix-like机器有效)

系统容量 = maxQps * minRt

maxQps :秒级统计出来的最大QPS

minRt:秒级统计出来的最小响应时间

RT

所有入口流量的平均RT达到阈值触发

线程数

所有入口流量的并发线程数达到阈值触发

入口QPS

所有入口流量的QPS达到阈值触发

授权规则

意思是这个API只允许那个微服务调用或者不被允许调用

SpringCloud Alibaba——Sentinel服务容错(学习记录)

代码方式配置

@GetMapping("test-add-flow-rule")
    public String testHot() {
        this.initFlowQpsRule();
        return "success";
    }

    private void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule("/shares/1");
        // set limit qps to 20
        rule.setCount(20);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setLimitApp("default");
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
           
SpringCloud Alibaba——Sentinel服务容错(学习记录)

Sentinel与控制台通信原理

其实是服务发现,微服务会注册到Sentinel控制台

SpringCloud Alibaba——Sentinel服务容错(学习记录)

这里的端口并不是微服务程序的端口,而是和sentinel的通信端口

SpringCloud Alibaba——Sentinel服务容错(学习记录)

在控制台中进行各类操作,其实就是调用了API去完成,API的地址可以通过机器列表上的IP+端口+/api来访问查看

SpringCloud Alibaba——Sentinel服务容错(学习记录)

还可以通过微服务自身的IP+端口+//actuator/sentinel来查看相关的信息

哪个IP,端口注册到sentinel上,心跳是10秒一次

SpringCloud Alibaba——Sentinel服务容错(学习记录)

控制台配置

应用端配置控制台

spring:
  cloud:
    sentinel:
      transport:
        # 指定sentinel控制台地址
        dashboard: localhost:8080
        # 指定和控制台通信IP
        # 不配置,就会自动选择一个IP注册
        client-ip: 127.0.0.1
        # 和控制台通信的端口,默认8719
        # 如果不设置,会从8719开始自动扫描,依次+1,直到找到未占用的端口
        port: 8719
        # 心跳发送周期,默认值为null
        # 在SimpleHttpHeartbeatSender会使用默认值10秒
        heartbeat-interval-ms: 10000
           

控制台配置

[1.6]表示自sentinel的1.6版本之后才有

配置项 默认值 默认值 描述
server.port 8080 - 指定端口
csp.sentinel.dashboard.server localhost:8080 - 指定地址
project.name 8080 - 指定程序的名称
sentinel.dashboard.auth.username [1.6] 8080 - Dashboard登录账号
sentinel.dashboard.auth.password [1.6] 8080 - Dashboard登录密码
server.servlet.session.timeout [1.6] 8080 - 登录Session过期时间,配置为7200表示7200秒,配置为60m表示60分钟

配置sentinel的账号密码

java -jar Dsentinel.dashboard.auth.username=XXX -Dsentinel.dashboard.auth.password = XXX   sentinel-dashboard-1.6.2.jar
           

Sentinel API

加了下面的配置,可以防止干扰

spring:
  cloud:
    sentinel:
      # 关闭最springMVC端点的保护
      filter:
        enabled: false
           

测试代码进行流控

1.添加流控之后,就会被限流

2.如果来源为test-wfw,那么就会被限流

3.这里的IllegalArgumentException异常也需要进程catch一下,不然的话实现不了流控

@GetMapping("/test-sentinel-api")
    public String testSentinelAPI(
        @RequestParam(required = false) String a) {

        String resourceName = "test-sentinel-api";
        ContextUtil.enter(resourceName, "test-wfw");

        // 定义一个sentinel保护的资源,名称是test-sentinel-api
        Entry entry = null;
        try {

            entry = SphU.entry(resourceName);
            // 被保护的业务逻辑
            if (StringUtils.isBlank(a)) {
                throw new IllegalArgumentException("a不能为空");
            }
            return a;
        }
        // 如果被保护的资源被限流或者降级了,就会抛BlockException
        catch (BlockException e) {
            log.warn("限流,或者降级了", e);
            return "限流,或者降级了";
        } catch (IllegalArgumentException e2) {
            // 统计IllegalArgumentException【发生的次数、发生占比...】
            Tracer.trace(e2);
            return "参数非法!";
        } finally {
            if (entry != null) {
                // 退出entry
                entry.exit();
            }
            ContextUtil.exit();
        }
    }
           

SphU: 最核心的,定义资源,让资源受到监控,保护资源

Tracer: 对我们想要的异常进行统计

ContextUtil: 可以实现调用来源,标记调用

更多可以参考文档

@SentinelResource使用

属性 作用 是否保修
value 资源名称
entryType entry类型,标记流量的方向,取值IN/OUT,默认是OUT
blockHandler 处理BlockException的函数名称。函数要求:1. 必须是 public2.返回类型与原方法一致3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。
blockHandlerClass 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。
fallback 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。
fallbackClass【1.6】 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
defaultFallback【1.6】 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:1. 返回类型与原方法一致2. 方法参数列表为空,或者有一个 Throwable 类型的参数。3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
exceptionsToIgnore【1.6】 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
exceptionsToTrace 需要trace的异常 Throwable
@GetMapping("/test-sentinel-resource")
    @SentinelResource(
        value = "test-sentinel-api",
        blockHandler = "block",
        blockHandlerClass = TestControllerBlockHandlerClass.class,
        fallback = "fallback"
    )
    public String testSentinelResource(@RequestParam(required = false) String a) {
        if (StringUtils.isBlank(a)) {
            throw new IllegalArgumentException("a cannot be blank.");
        }
        return a;
    }

    /**
     * 1.5 处理降级
     * - sentinel 1.6 可以处理Throwable
     *
     * @param a
     * @return
     */
    public String fallback(String a) {
        return "限流,或者降级了 fallback";
    }
           
@Slf4j
public class TestControllerBlockHandlerClass {

    /**
     * 处理限流活降级
     * @param a
     * @param e
     * @return
     */
    public static String block(String a, BlockException e) {
        log.warn("限流,或者降级了 block", e);
        return "限流,或者降级了 block";
    }
}
           

RestTemplate 使用sentinel

在初始化RestTemplate加上@SentinelRestTemplate即可

@Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
           
@Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test-rest-template-sentinel/{userId}")
    public UserDTO test(@PathVariable Integer userId) {
        return this.restTemplate
            .getForObject(
                "http://user-center/users/{userId}",
                UserDTO.class, userId);
    }
           

@SentinelRestTemplate也可以进行自定义的异常处理,他也包含blockHandler,blockHandlerClass,fallback,fallbackClass,用法跟上面的一样。

关闭@SentinelRestTEmplate,关闭了之后在sentinel控制台就看不到他的粗点链路。

resttemplate:
  sentinel:
    # 关闭@SentinelRestTEmplate注解
    enabled: true
           

Feign整合Sentinel

配置属性就可以了

feign:
  # 为feign整合sentinel
  sentinel:
    enabled: true
           

在feign的注释中可以加上fallback,这样可以处理限流之后的操作

@FeignClient(name = "user-center",
        fallback = UserCenterFeignClientFallback.class)
public interface UserCenterFeignClient {

    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable Integer id);
}
           

这样在返回的数据中,字段值就变成了一个默认用户,使用fallback是不会有异常捕获的

@Component
public class UserCenterFeignClientFallback implements UserCenterFeignClient {

    @Override
    public UserDTO findById(Integer id) {
        UserDTO userDTO = new UserDTO();
        userDTO.setWxNickname("一个默认用户");
        return userDTO;
    }
}
           

如果要捕获异常就可以使用fallbackFactory ,这里需要注意的是fallback和fallbackFactory 不能同时使用。

@FeignClient(name = "user-center",
        fallbackFactory = UserCenterFeignClientFallbackFactory.class)
public interface UserCenterFeignClient {

    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable Integer id);
}
           
@Component
@Slf4j
public class UserCenterFeignClientFallbackFactory implements FallbackFactory<UserCenterFeignClient> {
    @Override
    public UserCenterFeignClient create(Throwable throwable) {
        return new UserCenterFeignClient() {
            @Override
            public UserDTO findById(Integer id) {
                log.warn("远程调用被限流/降级了", throwable);
                UserDTO userDTO = new UserDTO();
                userDTO.setWxNickname("一个默认用户");
                return userDTO;
            }
        };
    }
}
           

规则持久化

拉模式

SpringCloud Alibaba——Sentinel服务容错(学习记录)

定时读取规则的json文件,如果发现有更新就会修改;

接收控制台的规则配置,写入json文件。

public class FileDataSourceInit implements InitFunc {
    @Override
    public void init() throws Exception {
        // TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
        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
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        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);
    }
}
           

在resources目录下创建:

META-INF/services/com.alibaba.csp.sentinel.init.InitFunc

com.alibaba.csp.sentinel.init.InitFunc文件内就是FileDataSourceInit 类的路径

优点:
  1. 易懂
  2. 没有多余依赖
缺点:
  1. 规则由FileRefreshableDataSource定时更新,更新时间有延迟,如果定时时间过长,那么规则更新延迟,反之影响性能;
  2. 规则文件在本地,如果迁移,需要将文件一起迁移,可能造成丢失。

推模式

SpringCloud Alibaba——Sentinel服务容错(学习记录)

将规则推送到远程配置中心

sentinel获取远程配置中心的规则,如果监听到规则有变化,就更新本地规则缓存。

如何配置推模式,工作量比较大,请参考博客

优点:
  1. 规则持久化
  2. 性能好
  3. 数据一致性
缺点:
  1. 构造复杂
  2. 引入依赖(服务发现组件)

集群流控

SpringCloud Alibaba——Sentinel服务容错(学习记录)

需要引入Token的依赖,微服务还要继承Token Client来实现

官方文档

sentinel错误页优化

sentinel提供了这个错误提示的处理接口 UrlBlockHandler

@Component
public class MyUrBlockHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        ErrorMsg msg = null;
        if (e instanceof FlowException) {
            msg = ErrorMsg.builder()
                    .status(100)
                    .msg("限流了")
                    .build();
        } else if (e instanceof DegradeException) {
            msg = ErrorMsg.builder()
                    .status(101)
                    .msg("降级了")
                    .build();
        } else if (e instanceof ParamFlowException) {
            msg = ErrorMsg.builder()
                    .status(102)
                    .msg("降级了")
                    .build();
        } else if (e instanceof SystemBlockException) {
            ErrorMsg.builder()
                    .status(103)
                    .msg("降级了")
                    .build();
        } else if (e instanceof AuthorityException) {
            msg = ErrorMsg.builder()
                    .status(104)
                    .msg("降级了")
                    .build();
        }
        httpServletResponse.setStatus(500);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        // spring mvc自带的json操作工具,叫jackson
        new ObjectMapper()
                .writeValue(
                        httpServletResponse.getWriter(),
                        msg
                );

    }
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class ErrorMsg {
    private Integer status;
    private String msg;
}
           
SpringCloud Alibaba——Sentinel服务容错(学习记录)

sentinel区分来源

sentinel提供RequestOriginParser接口

@Component
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 从请求参数中获取 origin的参数并返回
        // 如果获取不到origin参数,那么就抛异常
        String origin = request.getParameter("origin");
        if (StringUtils.isBlank(origin)) {
            throw new IllegalArgumentException("orgin must be specified");
        }
        return origin;
    }
}
           

实际中可以把来源放到header中

sentinel支持RESTful资源

sentinel提供UrlCleaner接口

@Component
public class MyUrlCleaner implements UrlCleaner {

    @Override
    public String clean(String s) {
        // 让share/1和share/2 返回值相同
        // 返回 share/{number}
        String[] split = s.split("/");

        return Arrays.stream(split)
                .map(string -> {
                    if (NumberUtils.isNumber(string)) {
                        return "{number}";
                    }
                    return s;
                })
                .reduce((a, b) -> a + "/" + b)
                .orElse("");
    }
}
           

sentinel配置总结

Spring Cloud Alibaba Sentienl相关配置项

配置项 含义 默认值
spring.cloud.sentinel.enabled Sentinel自动化配置是否生效 true
spring.cloud.sentinel.eager 取消Sentinel控制台懒加载 false
spring.cloud.sentinel.transport.port 应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer 8719
spring.cloud.sentinel.transport.dashboard Sentinel 控制台地址
spring.cloud.sentinel.transport.heartbeat-interval-ms 应用与Sentinel控制台的心跳间隔时间
spring.cloud.sentinel.transport.client-ip 客户端IP
spring.cloud.sentinel.filter.order Servlet Filter的加载顺序。Starter内部会构造这个filter Integer.MIN_VALUE
spring.cloud.sentinel.filter.url-patterns 数据类型是数组。表示Servlet Filter的url pattern集合 /*
spring.cloud.sentinel.filter.enabled Enable to instance CommonFilter true
spring.cloud.sentinel.metric.charset metric文件字符集 UTF-8
spring.cloud.sentinel.metric.file-single-size Sentinel metric 单个文件的大小
spring.cloud.sentinel.metric.file-total-count Sentinel metric 总文件数量
spring.cloud.sentinel.log.dir Sentinel 日志文件所在的目录
spring.cloud.sentinel.log.switch-pid Sentinel 日志文件名是否需要带上pid
spring.cloud.sentinel.servlet.block-page 自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL
spring.cloud.sentinel.flow.cold-factor 冷启动因子 3

详细参见大目老师手记

继续阅读