SpringBoot集成sentinel

Sentinel 介绍

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。

完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。

完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。

本文通过Sentinel_dashBoard进行讲解,当然不引入监控看板也能实现限流熔断降级功能,但是监控看板能够直观的看到请求的QPS,成功率等等,同时可以实时的进行降级限流策略的修改与新建。

sentinel_dashboard的引入

  https://github.com/alibaba/Sentinel/releases,本人下载sentinel-dashboard-1.8.0.jar

该项目是纯纯的springboot项目,在CMD模式下使用命令 

java -Dserver.port=8181 -Dcsp.sentinel.dashboard.server=localhost:8181

-Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar

其中,-Dserver.port=8181 代表sentinel控制台项目的端口号,

-Dcsp.sentinel.dashboard.server=localhost:8181 代表本看板服务将会注册到自己的看板上,

-Dproject.name=sentinel-dashboard代表本看板服务的项目名称。

访问localhost:8181;用户名,密码均是sentinel

如果要自定义用户名和密码,在启动命令加上-Dsentinel.dashboard.auth.username=sentinel,
-Dsentinel.dashboard.auth.password=123456即可。

可以看到自身的服务已经注册到了控制台上。

springboot工程快速接入dashboard

<!-- 限流、熔断框架 不连通客户端本地可用 -->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-core</artifactId>
   <version>1.8.2</version>
</dependency>

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-annotation-aspectj</artifactId>
   <version>1.8.2</version>
</dependency>

<!-- sentinel客户端与dashboard通信依赖 -->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-transport-simple-http</artifactId>
   <version>1.8.2</version>
</dependency>

 yml配置

spring:
  application:
    name: demo
  cloud:
    sentinel:
      transport:
        post: 8722  #跟控制台交流的端口,随意指定一个未使用的端口即可
        client-ip: localhost  
        dashboard: localhost:8181 #控制台地址

重启自己项目,回到控制台,,调用我们项目中的一个接口,,刷新控制台,,就会发现demo项目,里面会有项目中实时被调取的接口,在这里我们就可以去给想要接口去配置限流 降级 热点 权限的规则

现在在控制台配置的规则是非持久化的,当我们项目重启后,所创建的规则就会消失。

降级规则持久化mysql(慢调用)

实体类:
@Data
@TableName("degrade_rule")
public class DegradeRuleEntity {

    @TableId
    private Integer id;
    private String resourceName;
    private Double count;
    private Double slowRatioThreshold;
    private Integer minRequestAmount;
    private Integer statIntervalMs;
    private Integer timeWindow;
    private Date createTime;
    private Date updateTime;

}

 建表语句:

CREATE TABLE `degrade_rule` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `resource_name` varchar(256) NOT NULL COMMENT '资源名称',
  `count` double NOT NULL COMMENT '慢调用时长,单位 毫秒',
  `slow_ratio_threshold` double NOT NULL COMMENT '慢调用比例阈值',
  `min_request_amount` int(11) NOT NULL COMMENT '熔断触发的最小请求数',
  `stat_interval_ms` int(11) NOT NULL COMMENT '统计时长,单位 毫秒',
  `time_window` int(11) NOT NULL COMMENT '熔断时长,单位为 s',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_resource_name` (`resource_name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='熔断规则表';

熔断规则加载:

@Override
public Boolean updateDegradeRule() {
    List<DegradeRuleEntity> degradeRuleList = baseMapper.queryAllRule();
    if (!CollectionUtils.isEmpty(degradeRuleList)) {
        List<DegradeRule> rules = null;
        rules = degradeRuleList.stream().map(degradeRuleEntity -> {
            //资源名,即规则的作用对象
            return new DegradeRule(degradeRuleEntity.getResourceName())
                    // 熔断策略,支持慢调用比例/异常比例/异常数策略
                    .setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
                    //慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
                    .setCount(degradeRuleEntity.getCount())
                    // 熔断时长,单位为 s
                    .setTimeWindow(degradeRuleEntity.getTimeWindow())
                    // 慢调用比例阈值
                    .setSlowRatioThreshold(degradeRuleEntity.getSlowRatioThreshold())
                    //熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断
                    .setMinRequestAmount(degradeRuleEntity.getMinRequestAmount())
                    //统计时长(单位为 ms)
                    .setStatIntervalMs(degradeRuleEntity.getStatIntervalMs());

        }).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(rules)) {
            DegradeRuleManager.loadRules(rules);
        }
    }
    return true;
}

项目启动初始化降级规则:

@PostConstruct
void initDegradeRule(){

    if(degradeRuleService.updateDegradeRule()) {
        LOG.warn("-------------------初始化DegradeRule   success----------------------");
    }else {
        LOG.warn("-------------------初始化DegradeRule     error----------------------");
    }
}

 注:根据自己项目可对通过添加编辑时重新加载降级规则:  或者做定时任务监控数据库该表是否发生变化来加载规则()

@Scheduled(cron = "0 0/2 * * * ?")
public void loadDegradeRule() {
    LOG.info("进入degradeRule定时任务");

    List<DegradeRuleEntity> degradeRuleList = degradeRuleDao.queryAllRule();
    if (CollectionUtils.isEmpty(degradeRuleList)) {
        return;
    }

    String lastMd5Hex = stringRedisTemplate.opsForValue().get(RedisKeys.degradeRuleKey);
    if (StringUtils.isEmpty(lastMd5Hex)) {
        lastMd5Hex = "";
    }
    String newMd5Hex = DigestUtils.md5Hex(JSON.toJSONString(degradeRuleList));
    if (StringUtils.isBlank(newMd5Hex) || StringUtils.equals(lastMd5Hex, newMd5Hex)) {
        LOG.info("degradeRule无更新>>>>>>>>>>>>>>>>>>>");
        return;
    }
    LOG.info(">>>>>>>>>>>>>>>>>>>>>>>>>degradeRule更新");
    List<DegradeRule> rules = null;
    rules = degradeRuleList.stream().map(degradeRuleEntity -> {
        //资源名,即规则的作用对象
        return new DegradeRule(degradeRuleEntity.getResourceName())
                // 熔断策略,支持慢调用比例/异常比例/异常数策略
                .setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
                //慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
                .setCount(degradeRuleEntity.getCount())
                // 熔断时长,单位为 s
                .setTimeWindow(degradeRuleEntity.getTimeWindow())
                // 慢调用比例阈值
                .setSlowRatioThreshold(degradeRuleEntity.getSlowRatioThreshold())
                //熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断
                .setMinRequestAmount(degradeRuleEntity.getMinRequestAmount())
                //统计时长(单位为 ms)
                .setStatIntervalMs(degradeRuleEntity.getStatIntervalMs());

    }).collect(Collectors.toList());

    if (CollectionUtils.isNotEmpty(rules)) {
        DegradeRuleManager.loadRules(rules);
        stringRedisTemplate.opsForValue().set(RedisKeys.degradeRuleKey, newMd5Hex);
    }

    LOG.info("[DegradeRuleConfig] 熔断规则加载: " + rules);
}
@SentinelResource(value = "demo", fallback = "exceptionHandler")在所需要降级的接口加上该注解,,,value为数据库中的资源名   fallback为降级后所走的方法

至此,,springboot集成sentinel降级熔断持久化到数据库

如若我们想对sentinel设置的规则统一处理的话

例如我们在控制台发现某个接口有问题,我们想要给它来一些规则熔断,,我们可提前在项目中做统一处理

public class CustomUrlBlockHandler implements UrlBlockHandler{
    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {

        Map<String, Object> map = new HashMap<>();

        if (ex instanceof FlowException){
            map.put("code", 100);
            map.put("msg", "限流了");
        }

        else if (ex instanceof DegradeException){
            map.put("code", 100);
            map.put("msg", "降级了");
        }

        else if (ex instanceof ParamFlowException){
            map.put("code", 100);
            map.put("msg", "热点参数限流了");
        }

        else if (ex instanceof SystemBlockException){
            map.put("code", 100);
            map.put("msg", "系统规则(负载...) 不满足规则");
        }

        else if (ex instanceof AuthorityException){
            map.put("code", 100);
            map.put("msg", "授权规则不通过");
        }

        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Type", "application/json;charset=utf-8");
        response.setContentType("application/json;charset=utf-8");
        new ObjectMapper().writeValue(response.getWriter(), map);
    }

我们只需在项目启动初始化此方法

@PostConstruct
void initUrlBlockHandler() {
    LOG.warn("-------------------Sentinel统一异常处理初始化----------------------");
    WebCallbackManager.setUrlBlockHandler(new CustomUrlBlockHandler());
}

这样,,我们临时在控制台添加的一些规则,就会统一处理了,,当然,我们需跟前端商量好出现code: 100时所要处理的方法即可

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值