sentinel限流_项目模块中引入 Sentinel限流组件

项目模块中引入 Sentinel限流组件

59e93ebe3cb2f1a9b1565fe60d31df5f.png

一、Sentinel简介

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

  • Sentinel 具有以下特征:

    • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
    • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
    • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
    • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
  • 详情参见github部分链接:

    • 源码[1]
    • 源码说明文档[2]
    • demo[3]

二、项目集成:

在我们的xxx项目最开始之前提出了对于外部的访问需要能够做到访问控制,要控制调用方、调用频率。因此这边选择了使用开源的sentinel流控组件来实现功能需求。

1.依赖

Gradle 依赖:

7518936a07c546b3ba7de4b61af3eea7.png
  • 「spring-cloud-starter-alibaba-nacos-config」:实现项目可以以nacos作为配置中心。
  • 「com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel」:实现项目接入sentinel流控组件。
  • 「com.alibaba.csp:sentinel-datasource-nacos:1.5.2」:实现sentinel组件加载流控相关配置可以从nacos中获取。

2.配置

1)项目配置
  • A.设置nacos
020bb24495937751333f6d52f4277f86.png
设置nacos作为配置中心
  • B.配置文件
29e7c5ec0d8eab571bf7146340b38f1a.png
增加专门的sentinel相关的配置文件

配置文件详情如下:


spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            # 配置中心地址
            server-addr: localhost:8848
# 默认分组策略(最简单)
            group-id: DEFAULT_GROUP
            # 配置文件类型
            data-type: json
# 类似文件名
            data-id: ${spring.applicaiton.name:xxx-service}-flowrule
# 规则类型:流量控制
            rule-type: flow
        ds2:
          nacos:
            server-addr: localhost:8848
# file:
            group-id: DEFAULT_GROUP
            data-id: ${spring.applicaiton.name:xxx-service}-degraderule
            data-type: json
# file: 'classpath: sentinel/degraderule.json'
# file: './sentinel/degraderule.json'
# 熔断降级
            rule-type: degrade
        ds3:
          nacos:
            server-addr: localhost:8848
            group-id: DEFAULT_GROUP
            data-id: ${spring.applicaiton.name:xxx-service}-authorityrule
            data-type: json
# 黑白名单控制
            rule-type: authority
        ds4:
          nacos:
            server-addr: localhost:8848
            group-id: DEFAULT_GROUP
            data-id: ${spring.applicaiton.name:xxx-service}-systemrule
            data-type: json
# 系统自适应:模块级别总防
            rule-type: system
#        ds5:
#          file:
#            file: 'classpath: sentinel/param-flow.json'
##            file: './sentinel/param-flow.json'
#            rule-type: param-flow
      eager: true
      transport:
# sentinel 控制台地址
        dashboard: localhost:8080
Nacos配置

直接在nacos中新增配置即可,图1为新增,图2为新增后,图3为sentinel控制台加载nacos中配置文件,展示的实际的线上规则(支持重写后立即生效)

265fe9425af70374bbeb190a010a8033.png
图1
a00d40a6adc6bd131cc9f4385d4cf786.png
图2
9c33289dc5625157f3121cba29d2466d.png
图3

不同流控规则配置文件的可选字段可以参见github相关章节。

d02300ab49d3d6d02a9b1e19ba3bc49f.png

3.架构

上述配置完成后,需要在代码中做简单编码,统一设置调用方(orign或者说limitApp)和命中响应。

1)调用方配置
package com.xxx.xxx.xxx.filter.sentinel;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;

import com.alibaba.csp.sentinel.context.ContextUtil;

/**
 * sentinel web url 限流统一过滤器,针对直接以controller层的url进行限流处理的情况
 * 
 * @Author:WWG
 * @date:2020年9月16日
 * @Version:1.0
 */
@Order(1)
@WebFilter(urlPatterns = "/*", filterName = "sentinelAuthorityFilter")
public class SentinelAuthorityFilter implements Filter {

    @SuppressWarnings("unused")
    private static final Logger LOGGER = LoggerFactory.getLogger(SentinelAuthorityFilter.class);

    private static final String HTTP_HEAD_APP_ID_VALUE_DEAFAULT_ERROR = "errorAppId";// 缺省值,当调用方未声明自己时,默认给一个错误值
    private static final String HTTP_HEAD_APP_ID_KEY = "appId";// 约定值 存放在http请求中自定义head

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest hSRequest = (HttpServletRequest)request;

        // sentinel 设置限流入口origin(对应limitApp)
        String appId = hSRequest.getHeader(HTTP_HEAD_APP_ID_KEY);
        // sentinel 目前是真的粗,一个白名单,你orgin为null、为空,居然能够直接通过拦截 因此下方做一下适配
        appId = StringUtils.isBlank(appId) ? HTTP_HEAD_APP_ID_VALUE_DEAFAULT_ERROR : appId;
        // contextName 由自己定义,关键能唯一标识某一个方法 ,该过滤器为web 过滤器,因此,可以uri作为上下文名称
        String contextName = hSRequest.getRequestURI();

        ContextUtil.enter(contextName, appId);
        chain.doFilter(request, response);
        ContextUtil.exit();
    }
}
2)限流命中处理
package com.xxx.xxx.xxx.filter.sentinel;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.xxx.xxx.common.constant.PopularStringConstant;
import com.xxx.xxx.common.util.StreamUtil;

/**
 * 自定义SENTINEL限流响应
 * 
 * @Author:WWG
 * @date:2020年9月21日
 * @Version:1.0
 */

@Component
public class XXXUrlBlockHandler implements BlockExceptionHandler {

    private static final String COMMA = PopularStringConstant.COMMA;
    private static final String SNETINEL_TIP_DEFAULT = "请稍候再试!!!";
    private static final String SNETINEL_TIP_CHECK_OR_TRY_LATER = "请自查权限或稍候再试!!!";
    private static final String SNETINEL_WARNING_DEFAULT = "请停止发送请求,您的信息已被追踪!!!";
    private static final String SNETINEL_RULE_SYSTEM_BINGO = "请求被 系统规则 命中";
    private static final String SENTINEL_RULE_AUTHORITY_BINGO = "请求被 授权规则 命中";
    private static final String SENTINEL_RULE_FLOW_BINGO = "请求被 流控规则 命中";
    private static final String SENTINEL_RULE_DEGRADE_BINGO = "请求被 降级规则 命中";
    private static final String SENTINEL_RULE_PARAMFLOW_BINGO = "请求被 热点规则 命中";
    private static final String SENTINEL_RULE_BINGO = "请求被  SENTINEL 规则 命中";

    private static final Logger LOGGER = LoggerFactory.getLogger(WSNUrlBlockHandler.class);

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {

        String warning;
        String errorMsg;
        if (e instanceof AuthorityException) {
            // 授权规则命中
            warning = SENTINEL_RULE_AUTHORITY_BINGO;
            errorMsg = warning + COMMA + SNETINEL_WARNING_DEFAULT;
        } else if (e instanceof SystemBlockException) {
            // 流控规则命中
            warning = SNETINEL_RULE_SYSTEM_BINGO;
            errorMsg = warning + COMMA + SNETINEL_TIP_DEFAULT;
        } else if (e instanceof FlowException) {
            // 流控规则命中
            warning = SENTINEL_RULE_FLOW_BINGO;
            errorMsg = warning + COMMA + SNETINEL_TIP_DEFAULT;
        } else if (e instanceof DegradeException) {
            // 流控规则命中
            warning = SENTINEL_RULE_DEGRADE_BINGO;
            errorMsg = warning + COMMA + SNETINEL_TIP_DEFAULT;
        } else if (e instanceof ParamFlowException) {
            // 热点规则命中
            warning = SENTINEL_RULE_PARAMFLOW_BINGO;
            errorMsg = warning + COMMA + SNETINEL_TIP_DEFAULT;
        } else {
            // 其他规则命中
            warning = SENTINEL_RULE_BINGO;
            errorMsg = warning + COMMA + SNETINEL_TIP_CHECK_OR_TRY_LATER;
        }
        LOGGER.debug(warning);
        StreamUtil.writeResponse(response, errorMsg);
    }
}

三、实现

最终项目可以实现根据不同的调用方设置不同的限流策略:

1.例1

前文中设置了针对来源为ZCB的请求,在请求如下资源:/enterprises(http://127.0.0.1:8802/xxx/enterprises)时超过阈值(qps为2)就会被命中如图:

请求头中设置了来源方,设计由网关鉴权之后设置传递,保证可信度:

6f2b3d0b730afdd38a0c825e7f6abb4d.png
fb218a8a05b79f1c8ba1beb72dfdae3e.png

2.例2

新增一个授权规则(黑白名单规则):

6206e2e74e74cb9c9c3213ef13da8c64.png

Sentinel 控制台查看规则是否已生效:

188e1d5dd69b5a480dd000121faac3b8.png

工具调用:

e0fd49ce6ba85a5e39f14104c3b701b4.png

四、概述

Sentinel 功能还有很多,上述例子只是一小部分,而且随着项目的壮大,后续的功能会越来越强大。

1.一些问题点的折射

1)版本
  • 存在版本高版本不适配低版本的情况,这确实可以称做一个吐槽点,成熟的项目一般不会出现这种情况,不过从侧面也反应出这个项目的年轻与潜力,面对未来,充满信息即可,实在不行,源码自己拉下来改。
  • 还行,目前也就两次截屏就能看到所有,当然还不包括子模块中展开,是否还有子模块。
2)代码

代码严谨度目前不高,一开始使用的file类型读取sentinel规则,eclipse中直接跑能行,但是打成jar包后,源码中读取file的方法根本不适用于读取jar包中的文件,后来被逼着使用nacos加载配置,加载本地文件相关问题代码如图:

五、待续。。。。

后续有更多的发掘会更新进来

Reference

[1]

github源码: https://github.com/alibaba/Sentinel

[2]

github源码对应的说明文档: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

[3]

spring-cloud-alibaba集成sentinel的demo: https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值