Sentinel不使用控制台基于注解限流,热点参数限流

news2024/12/23 22:14:09

目录

一、maven依赖

二、控制台

三、基于注解限流

四、热点参数限流

五、使用JMeter验证


一、maven依赖

需要注意,使用的版本需要和你的SpringBoot版本匹配!!

Spring-Cloud直接添加如下依赖即可,baba已经帮你指定好版本了。

当然可以点进去spring-cloud-starter-alibaba-sentinel搜索sentinel-core对应的版本

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

如果是SpringBoot的话,需要手动导入包。

我这里使用的是SpringBoot Version 2.2.5.RELEASE,对应的版本是1.7.1 (你要根据自己SpringBoot版本查询对应版本,如果版本不对应的话根本用不了)

<sentinel.version>1.7.1</sentinel.version>

<!--sentinel核心包-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
</dependency>
<!--sentinel注解支持模块-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>${sentinel.version}</version>
</dependency>
<!--sentinel与控制台交互-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
<version>${sentinel.version}</version>
</dependency>

二、控制台

这里也介绍下控制台的使用方法,但我的项目是没运维给我安装这个东西,就没法用它。。

下载地址:Release v1.7.1 · alibaba/Sentinel · GitHub

使用命令启动jar包

java -jar sentinel-dashboard-17.1.jar

前端访问:localhost:8080, 账号密码 默认都是 sentinel

但是但是!!!!!!!界面是空的!!!!!!!!!

启动命令需要指定你的项目的端口,以及名称才行:

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dcsp.sentinel.api.port=8998 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar

以下是各个命令的介绍:

-Dserver.port=9000  运行端口

-Dcsp.sentinel.dashboard.server=localhost:9000  浏览器访问地址

-Dproject.name=sentinel-dashboard     项目名称

-Dcsp.sentinel.api.port=8092  sentinel客户端端口

-Dsentinel.dashboard.auth.username=username,设置用户名

-Dsentinel.dashboard.auth.password=password,设置访问密码

三、基于注解限流

我的项目没法使用控制台进行动态修改规则,只能在数据库表里面写好规则,系统启动的时候进行读取(当然可以动态修改规则无须重启)

1、maven引入依赖:

<!--sentinel核心包-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>${sentinel.version}</version>
</dependency>
<!--sentinel注解支持模块-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>${sentinel.version}</version>
</dependency>

2、创建SentinelConfig类,启用注解

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Slf4j
@Configuration
public class SentinelConfig {

    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}

3、创建表 system_sentinel_config 规则配置表

DROP TABLE IF EXISTS `system_sentinel_config`;
CREATE TABLE `system_sentinel_config` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
    `module_name` varchar(100) NOT NULL COMMENT '资源名称',
    `resource` varchar(100) NOT NULL COMMENT '资源名称(唯一), 对应{@link SentinelResName}',
    `desc` varchar(100) NOT NULL COMMENT '资源描述',
    `grade` tinyint(1) NOT NULL DEFAULT 1 COMMENT '阈值类型, 0: 线程数, 1: QPS',
    `count` bigint(20) NOT NULL COMMENT '如果grade为线程数, 表示每秒最高线程数。如果grade为QPS, 表示每秒最高访问量。',
    `strategy` tinyint(1) NOT NULL DEFAULT 0 COMMENT '流控模式。0:直接, 1: 关联, 2:链路',
    `control_behavior` tinyint(1) NOT NULL DEFAULT 0 COMMENT '流控效果。0:默认(直接拒绝), 1: Warm Up(冷启动), 2:匀速排队 3: 冷启动+匀速排队',
    `max_queueing_time_ms` int DEFAULT NULL COMMENT '流控效果为匀速排队时,队列的排队时间(单位:毫秒),需要注意这里类型是int, 并且单位是毫秒',
    `warm_up_period_sec` int DEFAULT NULL COMMENT '冷启动到达最大值的时间(单位:秒)',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '创建时间',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '更新时间',
    `create_user` VARCHAR(50) DEFAULT NULL COMMENT '创建人' ,
    `update_user` VARCHAR(50) DEFAULT NULL COMMENT '更新人' ,
    PRIMARY KEY (`id`),
    UNIQUE KEY resource_key(`resource`)
) ENGINE=InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET=utf8 COMMENT='sentinel限流配置表';

-- 标准对接--档案类
INSERT INTO `system_sentinel_config` (`module_name`, `resource`, `desc`, `grade`, `count`, `strategy`, `control_behavior`, `max_queueing_time_ms`, `warm_up_period_sec`) VALUES
('标准对接--档案类', 'standard:structure:add',     '新增/编辑建筑类节点(片区/楼栋/单元/住家)', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:structure:delete',  '删除建筑类节点(片区/楼栋/单元/住家)', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:file:upload',       '上传图片', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:check:face',        '人脸质量校验', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:household:add',     '新增/编辑住户(业主,租户)', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:household:delete',  '删除住户', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:visitor:add',       '新增/编辑访客', 1, 20, 0, 0, null, null),
('标准对接--档案类', 'standard:visitor:delete',    '删除访客', 1, 20, 0, 0, null, null);

-- 标准对接--其它
INSERT INTO `system_sentinel_config` (`module_name`, `resource`, `desc`, `grade`, `count`, `strategy`, `control_behavior`, `max_queueing_time_ms`, `warm_up_period_sec`) VALUES
('标准对接--其它', 'standard:remote:open',         '第三方远程开门', 1, 20, 0, 0, null, null),
('标准对接--其它', 'standard:smart:lock:control',  '第三方锁门禁权限冻结/解冻', 1, 20, 0, 0, null, null),
('标准对接--其它', 'standard:smart:lock:set:temporary:pwd',    '第三方锁临时密码下发', 1, 20, 0, 0, null, null);

4、对应的SystemSentinelConfigDO对象

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.whysu.scd.base.common.constant.sentinel.SentinelResName;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * sentinel限流配置表(SystemSentinelConfig)实体类
 */
@Data
public class SystemSentinelConfigDO {
    /**
     * 主键id
     */
    private Long id;
     /**
     * 资源名称
     */
    private String moduleName;

    /**
     * 资源名称(唯一), 对应{@link SentinelResName}
     */
    private String resource;

    /**
     * 资源描述
     */
    private String desc;

    /**
     * 阈值类型
     * 0: 线程数 {@link RuleConstant#FLOW_GRADE_THREAD},
     * 1: QPS {@link RuleConstant#FLOW_GRADE_QPS}
     */
    private Integer grade;

    /**
     * 如果grade为线程数, 表示每秒最高线程数。
     * 如果grade为QPS, 表示每秒最高访问量。
     */
    private Long count;

    /**
     * 流控模式。
     * 0:直接{@link RuleConstant#STRATEGY_DIRECT},
     * 1:关联{@link RuleConstant#STRATEGY_RELATE},
     * 2:链路{@link RuleConstant#STRATEGY_CHAIN},
     */
    private Integer strategy;

    /**
     * 流控效果。
     * 0:默认(直接拒绝){@link RuleConstant#CONTROL_BEHAVIOR_DEFAULT},
     * 1: Warm Up(冷启动){@link RuleConstant#CONTROL_BEHAVIOR_WARM_UP},
     * 2:匀速排队{@link RuleConstant#CONTROL_BEHAVIOR_RATE_LIMITER},
     * 3: 冷启动+匀速排队{@link RuleConstant#CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER},
     */
    private Integer controlBehavior;

    /**
     * 流控效果为匀速排队时,队列的排队时间(单位:毫秒), 需要注意这里类型是int, 并且单位是毫秒
     */
    private Integer maxQueueingTimeMs;

    /**
     * 冷启动到达最大值的时间(单位:秒)
     */
    private Integer warmUpPeriodSec;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 创建人(手机号码)
     */
    private String createUser;

    /**
     * 更新人(手机号码)
     */
    private String updateUser;

}

5、系统启动的时候,进行加载。这里的主要方法是。

FlowRuleManager.loadRules(rules);

   public void initFlowRule() {
        // 查询配置
        List<SystemSentinelConfigDO> list = systemSentinelConfigService.selectList();
        if (CollectionUtils.isEmpty(list)) {
            log.error("sentinel限流配置为空!!!!");
            return;
        }
        List<FlowRule> rules = new ArrayList<>();
        for (SystemSentinelConfigDO configDO : list) {
            rules.add(SentinelUtil.getFlowRule(configDO));
        }
        // 加载
        FlowRuleManager.loadRules(rules);
    }

SentinelUtil工具类:

public class SentinelUtil {

    public static void update(List<SystemSentinelConfigDO> list) {
        List<FlowRule> rules = new ArrayList<>();
        for (SystemSentinelConfigDO configDO : list) {
            rules.add(SentinelUtil.getFlowRule(configDO));
        }
        // 加载
        FlowRuleManager.loadRules(rules);
    }

    public static List<FlowRule> getFlowRuleList() {
        return FlowRuleManager.getRules();
    }

    public static FlowRule getFlowRule(SystemSentinelConfigDO configDO) {
        // 限流规则
        FlowRule rateRule = new FlowRule();
        // 设置资源名,即流量控制规则的作用对象
        rateRule.setResource(configDO.getResource());
        // 设置限流阈值
        rateRule.setCount(configDO.getCount());
        // 限流阈值类型
        rateRule.setGrade(configDO.getGrade());
        // 流控模式
        rateRule.setStrategy(configDO.getStrategy());
        // 流控效果
        rateRule.setControlBehavior(configDO.getControlBehavior());
        // 流控效果为匀速排队时,队列的排队时间(单位:毫秒), 需要注意这里类型是int, 并且单位是毫秒
        if (configDO.getMaxQueueingTimeMs() != null && configDO.getMaxQueueingTimeMs() != 0) {
            rateRule.setMaxQueueingTimeMs(configDO.getMaxQueueingTimeMs());
        }
        // 冷启动到达最大值的时间(单位:秒)
        if (configDO.getWarmUpPeriodSec() != null && configDO.getWarmUpPeriodSec() != 0) {
            rateRule.setWarmUpPeriodSec(configDO.getWarmUpPeriodSec());
        }
        return rateRule;
    }
}

6、通过update方法动态更新:

    @Transactional
    @Override
    public void update(SystemSentinelConfigDO configDO) {
        ValidateUtils.checkBlank(configDO.getResource());
        SystemSentinelConfigDO dbDO = systemSentinelConfigDao.selectByResource(configDO.getResource());
        if (dbDO != null) {
            configDO.setId(dbDO.getId());
            systemSentinelConfigDao.update(configDO);
        }
        // 全部更新
        SentinelUtil.update(systemSentinelConfigDao.selectList(null));
    }

7、定义限流通用返回, 这里的名称是standardLimit

@Slf4j
public class StandardBlockHandler {

    /**
     * 标准对接, 触发限流时返回
     */
    public static ThirdResponseDTO<Object> standardLimit(BlockException ex) {
        return ResponseThirdHelper.failResponse(ResultCodeThirdEnum.THIRD_STANDARD_RATE_LIMIT);
    }
}

8、Controller接口通过@SentinelResource指定资源名称,以及触发限流时的返回消息

@RestController
@RequestMapping("/third/archives")
@Slf4j
@RequiredArgsConstructor
public class StandardThirdArchivesController {

    private final StandardArchivesService standardArchivesService;

    /**
     * 新增/编辑建筑类节点(片区/楼栋/单元/住家)
     */
    @PostMapping("/structure/addEdit")
    @SentinelResource(value = "standard:structure:add", blockHandler = "standardLimit", blockHandlerClass = {StandardBlockHandler.class})
    public ThirdResponseDTO<StandardStructureAddRes> addEditStructure(@RequestBody @Validated ThirdRequestDTO<StandardStructureAddReq> req) {
        return ResponseThirdHelper.successResponse(standardArchivesService.addEditStructure(req.getData(), req.getNeighNo()));
    }
}

四、热点参数限流

很遗憾,前面说的第三点【基于注解限流】的方式,没法具体到热点参数,因为我的项目的“入参”是有些复杂的对象,但热点参数只支持【基本类型】的入参。。。

1、第三点说的【基于注解限流】直接作废,重新来过

2、maven引入依赖

<!--sentinel核心包-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>${sentinel.version}</version>
</dependency>
<!--sentinel热点参数限流-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>${sentinel.version}</version>
</dependency>

 3、我这里的入参 ThirdRequestDTO 里面有个字段 clientId,是我需要限流的热点参数:

@Data
public class ThirdRequestDTO<T> {

    /**
     * 授权分配的clientId
     */
    private String clientId;

}

4、因为热点参数只支持QPS,于是可以配置的选项就很少了:

以下是配置表:standard_sentinel_config (需要注意sql查询的时候,desc, count要加单引号。你也可以给这2个参数重命名)

这里表示在duration_in_sec(秒)内,访问resource(资源)超过count (次数) 就进行限流,

如果是热点参数的话,则表示:

在duration_in_sec(秒)内,访问resource(资源)超过client_id_count(次数) 就进行限流,

DROP TABLE IF EXISTS `standard_sentinel_config`;
CREATE TABLE `standard_sentinel_config` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
    `resource` varchar(100) NOT NULL COMMENT '资源名称{@link SentinelResName}',
    `desc` varchar(100) NOT NULL COMMENT '资源描述',
    `duration_in_sec` int NOT NULL COMMENT '限流的单位时间(秒)',
    `count` int NOT NULL COMMENT '限流参数',
    `client_id_count` int NOT NULL COMMENT '针对clientId限流参数',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '创建时间',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP  COMMENT '更新时间',
    create_user VARCHAR(50) DEFAULT NULL COMMENT '创建人' ,
    update_user VARCHAR(50) DEFAULT NULL COMMENT '更新人' ,
    PRIMARY KEY (`id`),
    UNIQUE KEY `resource_key`(`resource`)
) ENGINE=InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET=utf8 COMMENT='标准对接限流';

-- 公共字典
INSERT INTO `standard_sentinel_config` (`resource`, `desc`, `duration_in_sec`, `count`, `client_id_count`) VALUES
('standard:archives',        '标准对接--档案类', 1, 60, 10),
('standard:query:archives',  '标准对接--查询档案类', 1, 60, 10),
('standard:other',           '标准对接--其它', 1, 60, 10);

5、StandardSentinelConfigDO对象

@Data
public class StandardSentinelConfigDO {
    /**
     * 主键id
     */
    private Long id;
    /**
     * 资源名称{@link SentinelResName}
     */
    private String resource;

    /**
     * 资源描述
     */
    private String desc;

    /**
     * 限流的单位时间(秒)
     */
    private Integer durationInSec;

    /**
     * 限流参数
     */
    private Integer count;

    /**
     * 针对clientId限流参数
     */
    private Integer clientIdCount;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 创建人(手机号码)
     */
    private String createUser;

    /**
     * 更新人(手机号码)
     */
    private String updateUser;

}

6、系统启动的时候,加载规则:(主要是通过ParamFlowRuleManager.loadRules)

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.whysu.scd.base.common.enums.ResultCodeEnum;
import com.whysu.scd.base.common.exception.BusinessException;
import com.whysu.scd.base.common.util.ValidateUtils;
import com.whysu.scd.module.standard.dao.StandardPlatformInfoDao;
import com.whysu.scd.module.standard.dao.StandardSentinelConfigDao;
import com.whysu.scd.module.standard.entity.StandardSentinelConfigDO;
import com.whysu.scd.module.standard.service.StandardSentinelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 标准对接--限流
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StandardSentinelServiceImpl implements StandardSentinelService {
    private final StandardPlatformInfoDao standardPlatformInfoDao;
    private final StandardSentinelConfigDao standardSentinelConfigDao;

    @Override
    public void init() {
        // 查询sentinel配置
        List<StandardSentinelConfigDO> configList = standardSentinelConfigDao.selectList(null);
        if (CollectionUtils.isEmpty(configList)) {
            log.error("标准对接sentinel配置为空!");
            return;
        }
        // 查询所有的clientId
        List<String> clientIdList = standardPlatformInfoDao.selectClientIdList2();

        List<ParamFlowRule> ruleList = new ArrayList<>();
        for (StandardSentinelConfigDO configDO : configList) {
            // 资源名
            ParamFlowRule rule = new ParamFlowRule(configDO.getResource());
            // 指定当前 rule 对应的热点参数索引
            rule.setParamIdx(0);
            // 限流的维度,该策略针对 QPS 限流
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            // 限流的单位时间(秒)
            rule.setDurationInSec(configDO.getDurationInSec());
            // 未使用指定热点参数时,该资源限流大小为50
            rule.setCount(configDO.getCount());
            // 热点参数限流
            if (CollectionUtils.isNotEmpty(clientIdList)) {
                List<ParamFlowItem> itemList = new ArrayList<>();
                for (String clientId : clientIdList) {
                    // item1 设置了clientId的限流,单位时间(DurationInSec)内只能访问10次
                    ParamFlowItem item1 = new ParamFlowItem().setObject(clientId) // 热点参数 value
                            .setClassType(String.class.getName()) // 热点参数数据类型
                            .setCount(configDO.getClientIdCount()); // 针对该value的限流值
                    itemList.add(item1);
                }
                rule.setParamFlowItemList(itemList);
            }
            ruleList.add(rule);
        }
        // 加载
        ParamFlowRuleManager.loadRules(ruleList);
    }

    @Override
    public void update(StandardSentinelConfigDO configDO) {
        ValidateUtils.checkBlank(configDO.getResource());
        // 查询
        StandardSentinelConfigDO dbDO = standardSentinelConfigDao.selectByResource(configDO.getResource());
        if (dbDO == null) {
            throw new BusinessException(ResultCodeEnum.PUTIAN_PARAM_ERROR);
        }
        // 更新
        configDO.setId(dbDO.getId());
        standardSentinelConfigDao.update(configDO);
        // 重新初始化
        init();
    }

    @Override
    public List<ParamFlowRule> getRuleList() {
        return ParamFlowRuleManager.getRules();
    }

    @Override
    public List<ParamFlowRule> getRuleOfResource(String resource) {
        return ParamFlowRuleManager.getRulesOfResource(resource);
    }
}

7、定义了一个注解:StandardSignature 

@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface StandardSignature {
    /**
     * 签名算法版本 2.0
     */
    String version() default ThirdConstant.SignatureVersion.TWO;
    /**
     * 接口权限
     */
    StandardOpenTypeEnum[] openType();
    /**
     * data参数是否不能为空(默认为true表示不能为空)
     */
    boolean dataNotNull() default true;
}

8、针对该注解切面:

这里的核心代码是:

Entry entry = null;
try {
    // 调用限流
    entry = SphU.entry(resourceName, EntryType.IN, 1, clientId);
    // 业务代码...
    return proceedingJoinPoint.proceed();
} catch (BlockException e) {
    // 接口限流
    return ResponseThirdHelper.failResponse(ResultCodeThirdEnum.THIRD_STANDARD_RATE_LIMIT);
} finally {
    if (entry != null) {
        entry.exit(1, clientId);
    }
}

有2个入参:resourceName和clientId

其中clientId是入参ThirdRequestDTO里面的。

resourceName是通过注解指定的StandardSignature里的opernType参数,查询出来的:

/**
     * key: 标准对接接口,value: sentinel限流资源名称{@link SentinelResName}
     */
    public static final HashMap<StandardOpenTypeEnum, String> SENTINEL_RESOURCE = new HashMap<StandardOpenTypeEnum, String>() {
        {
            // 标准对接-档案类
            put(StandardOpenTypeEnum.ARCHIVES, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_GET_CALL_NO_FIRST4, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_STRUCT_ADD, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_STRUCT_DELETE, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_UPLOAD_FACE_FILE, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_CHECK_FACE, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_HOUSEHOLD_ADD, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_HOUSEHOLD_DELETE, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_VISITOR_ADD, SentinelResName.STANDARD_ARCHIVES);
            put(StandardOpenTypeEnum.ARCHIVES_VISITOR_DELETE, SentinelResName.STANDARD_ARCHIVES);
            // 标准对接-查询档案类
            put(StandardOpenTypeEnum.QUERY_ARCHIVES, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_NEIGH, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_TREE_WHOLE, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_TREE_PAGE, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_AREA, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_BUILDING, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_UNIT, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_HOUSE, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_DEVICE, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_DEVICE_STATUS, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_PARKING_INFO, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_PARKING_ROAD_INFO, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_PARKING_CAR_INFO, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_PERSON_INFO, SentinelResName.STANDARD_QUERY_ARCHIVES);
            put(StandardOpenTypeEnum.QUERY_ARCHIVES_DEVICE_INFO, SentinelResName.STANDARD_QUERY_ARCHIVES);
            // 标准对接-其它
            put(StandardOpenTypeEnum.OTHER, SentinelResName.STANDARD_OTHER);
            put(StandardOpenTypeEnum.OTHER_REMOTE_OPEN, SentinelResName.STANDARD_OTHER);
            put(StandardOpenTypeEnum.OTHER_BLOCK_CONTROL, SentinelResName.STANDARD_OTHER);
            put(StandardOpenTypeEnum.OTHER_SEND_TEMPORARY_PWD, SentinelResName.STANDARD_OTHER);
        }
    };

其中SentinelResName (对应standard_sentinel_config表的resources字段)

public class SentinelResName {
    /**
     * 标准对接--档案类
     */
    public static final String STANDARD_ARCHIVES = "standard:archives";
    /**
     * 标准对接--查询档案类
     */
    public static final String STANDARD_QUERY_ARCHIVES = "standard:query:archives";
    /**
     * 标准对接--其它
     */
    public static final String STANDARD_OTHER = "standard:other";

}

OpenType是用的枚举:

import com.whysu.scd.module.standard.dto.StandardTreeNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;

/**
 * 允许第三方调用的接口类型集合
 */
@Getter
@Accessors(fluent = true)
@AllArgsConstructor
public enum StandardOpenTypeEnum {
    // 全部接口
    ALL(1, 0, "全部接口"),
    // 二级
    ARCHIVES(2, ALL.type, "档案类"),
    QUERY_ARCHIVES(3, ALL.type, "查询档案类"),
    QUERY_RECORD(4, ALL.type, "查询记录"),
    OTHER(5, ALL.type, "其它接口"),

    // 档案类,取值范围:100到199
    ARCHIVES_GET_CALL_NO_FIRST4(100, ARCHIVES.type, "获取呼叫号码前四位"),
    ARCHIVES_STRUCT_ADD(101, ARCHIVES.type, "新增/编辑片区/楼栋/单元/住家"),
    ARCHIVES_STRUCT_DELETE(102, ARCHIVES.type, "删除片区/楼栋/单元/住家"),
    ARCHIVES_UPLOAD_FACE_FILE(103, ARCHIVES.type, "上传人脸"),
    ARCHIVES_CHECK_FACE(104, ARCHIVES.type, "人脸质量校验"),
    ARCHIVES_HOUSEHOLD_ADD(105, ARCHIVES.type, "新增/编辑住户(会进行人脸质量校验)"),
    ARCHIVES_HOUSEHOLD_DELETE(106, ARCHIVES.type, "删除住户"),
    ARCHIVES_VISITOR_ADD(107, ARCHIVES.type, "新增/编辑访客(会进行人脸质量校验)"),
    ARCHIVES_VISITOR_DELETE(108, ARCHIVES.type, "删除访客"),

    // 查询档案类,取值范围:200到299
    QUERY_ARCHIVES_NEIGH(200, QUERY_ARCHIVES.type, "查询小区档案"),
    QUERY_ARCHIVES_TREE_WHOLE(201, QUERY_ARCHIVES.type, "查询树结构(全部)"),
    QUERY_ARCHIVES_TREE_PAGE(202, QUERY_ARCHIVES.type, "查询树结构(分页)(小区片区楼栋单元住家地点)"),
    QUERY_ARCHIVES_AREA(203, QUERY_ARCHIVES.type, "查询片区档案(分页)"),
    QUERY_ARCHIVES_BUILDING(204, QUERY_ARCHIVES.type, "查询楼栋档案(分页)"),
    QUERY_ARCHIVES_UNIT(205, QUERY_ARCHIVES.type, "查询单元档案(分页)"),
    QUERY_ARCHIVES_HOUSE(206, QUERY_ARCHIVES.type, "查询房屋档案(分页)"),
    QUERY_ARCHIVES_DEVICE(207, QUERY_ARCHIVES.type, "查询设备档案(分页)"),
    QUERY_ARCHIVES_DEVICE_STATUS(208, QUERY_ARCHIVES.type, "查询设备在离线状态(分页)"),
    QUERY_ARCHIVES_PARKING_INFO(209, QUERY_ARCHIVES.type, "查询车场档案"),
    QUERY_ARCHIVES_PARKING_ROAD_INFO(210, QUERY_ARCHIVES.type, "查询车道档案(分页)"),
    QUERY_ARCHIVES_PARKING_CAR_INFO(211, QUERY_ARCHIVES.type, "查询车辆档案(分页)"),
    QUERY_ARCHIVES_PERSON_INFO(212, QUERY_ARCHIVES.type, "查询人员档案(住户/访客/工作人员)(分页)"),
    QUERY_ARCHIVES_DEVICE_INFO(213, QUERY_ARCHIVES.type, "查询设备详情"),


    // 查询记录类,取值范围:300到399
    QUERY_RECORD_ACCESS_PAGE(300, QUERY_RECORD.type, "查询门禁记录(分页,不能跨月查询)"),
    QUERY_RECORD_TALK_PAGE(301, QUERY_RECORD.type, "查询对讲记录(分页,不能跨月查询)"),
    QUERY_RECORD_CAR_PAGE(302, QUERY_RECORD.type, "查询车辆出入记录(分页,不能跨月查询)"),

    // 其它接口,取值范围:400到499
    OTHER_REMOTE_OPEN(400, OTHER.type, "第三方远程开门"),
    OTHER_BLOCK_CONTROL(401, OTHER.type, "锁门禁权限冻结/解冻"),
    OTHER_SEND_TEMPORARY_PWD(402, OTHER.type, "锁临时密码下发"),

    ;

    /**
     * 允许第三方调用的接口类型
     */
    private final int type;
    /**
     * 父节点接口类型
     */
    private final int parentType;
    /**
     * 描述
     */
    private final String desc;

}

完整的切面代码如下:

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.whysu.scd.base.common.constant.i18n.third.standard.MessageThirdStandard;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 标准对接-数字签名校验切面
 */
@Aspect
@Component
@Slf4j
@Configuration
public class StandardSignatureValidateAspect {

    @Pointcut(value = "@annotation(com.whysu.scd.module.standard.annotation.StandardSignature)")
    public void signaturePointcut() {

    }

    @Around("signaturePointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        StandardSignature annotation = signature.getMethod().getAnnotation(StandardSignature.class);
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof ThirdRequestDTO) {
                ThirdRequestDTO requestDTO = (ThirdRequestDTO) arg;
                String clientId = requestDTO.getClientId();
                // 根据openType获取资源名称
                Set<String> resourceSet = new HashSet<>();
                for (StandardOpenTypeEnum openTypeEnum : annotation.openType()) {
                    String resourceName = StandardMap.SENTINEL_RESOURCE.get(openTypeEnum);
                    if (StringUtils.isNotBlank(resourceName)) {
                        resourceSet.add(resourceName);
                    }
                }
                // 限流判断
                for (String resourceName : resourceSet) {
                    Entry entry = null;
                    try {
                        // 调用限流
                        entry = SphU.entry(resourceName, EntryType.IN, 1, clientId);
                        // 业务代码...
                        return proceedingJoinPoint.proceed();
                    } catch (BlockException e) {
                        // 接口限流
                        return ResponseThirdHelper.failResponse(ResultCodeThirdEnum.THIRD_STANDARD_RATE_LIMIT);
                    } finally {
                        if (entry != null) {
                            entry.exit(1, clientId);
                        }
                    }
                }
            }
        }
        return null;
    }


    @AfterReturning(returning = "ret", pointcut = "signaturePointcut()")
    public void doAfterReturning(Object ret) {
        // 处理完请求,返回内容
        log.info("标准对接请求scd, 响应参数为:{}", JacksonUtils.toJson(ret));
    }

}

9、在Controller接口指定注解:

    /**
     * 新增/编辑建筑类节点(片区/楼栋/单元/住家)
     */
    @StandardSignature(openType = StandardOpenTypeEnum.ARCHIVES_STRUCT_ADD)
    @PostMapping("/structure/addEdit")
    public ThirdResponseDTO<StandardStructureAddRes> addEditStructure(@RequestBody @Validated ThirdRequestDTO<StandardStructureAddReq> req) {
        return ResponseThirdHelper.successResponse(standardArchivesService.addEditStructure(req.getData(), req.getNeighNo()));
    }

五、使用JMeter验证

首先声明这个工具我不大会用哈哈哈

1、先下载http://jmeter.apache.org/download_jmeter.cgi

2、解压,进入bin目录下执行jmeter.bat即可启动成功。

3、默认语言是英文,进入bin目录修改 jmeter.properties,添加如下配置即可改成中文

language=zh_CN

4、右键添加--线程(用户)-线程组

配置1秒并发100次

5、线程组右键:添加--取样器--HTTP请求

指定请求协议,地址,端口,方式,路径。可以直接在【消息体数据】里面复制json对象。

6、HTTP请求右键:添加--配置元件--HTTP信息头管理器

添加请求头:Content-Type   application/json;charset=utf-8

7、HTTP请求右键:添加--断言--响应断言

我这里也不知道咋写,但是我知道我项目的响应码3011是对应限流。

我这里就设置成,响应文本,包含3011,就表示成功

8、线程组右键:添加--监听器--察看结果树

9、线程组右键:启动

查看结果树,被限流了哈哈哈哈^_^、、、。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1789244.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

沃尔玛、美客多跨境平台自养号全攻略:防关联环境系统搭建与养号技巧

在沃尔玛、美客多等跨境平台进行自养号的过程中&#xff0c;环境系统的选择和账号的养育是至关重要的。以下是我对这两个方面的经验和技巧的总结&#xff1a; 环境系统&#xff1a; 市面上有很多环境系统可供选择&#xff0c;但质量参差不齐。为了实现足够高的伪装度&#xff…

服务器数据恢复—raid5阵列上分配的卷被删除后重建如何恢复被删除卷的数据?

服务器存储数据恢复环境&#xff1a; 某品牌FlexStorage P5730服务器存储&#xff0c;存储中有一组由24块硬盘组建的RAID5阵列&#xff0c;包括1块热备硬盘。 服务器存储故障&#xff1a; 存储中的2个卷被删除&#xff0c;删除之后重建了一个新卷。需要恢复之前删除的一个卷的数…

高舒适性气膜网球馆的注意事项—轻空间

气膜网球馆以其高舒适性、智能恒温等特点&#xff0c;成为现代体育场馆的新宠。在国家提倡全民健身的背景下&#xff0c;各地气膜网球馆如雨后春笋般涌现。然而&#xff0c;在建设和使用气膜网球馆时&#xff0c;需要特别注意以下几点&#xff1a; 1. 避免随意拆装 气膜网球馆…

Three.js——tween动画、光线投射拾取、加载.obj/.mtl外部文件、使用相机控制器

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

[数据集][目标检测]剪刀石头布检测数据集VOC+YOLO格式1973张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1973 标注数量(xml文件个数)&#xff1a;1973 标注数量(txt文件个数)&#xff1a;1973 标注…

SpringBoot接口防抖(防重复提交)

SpringBoot接口防抖&#xff08;防重复提交&#xff09; 概念 Spring Boot接口防抖&#xff08;Debouncing&#xff09;的概念是指在处理请求时&#xff0c;通过一定的机制来防止用户频繁触发同一接口请求&#xff0c;以防止重复提交或频繁请求的情况发生。 在Web应用中&…

Jupyter Notebook 切换虚拟环境

具体流程&#xff1a; 1. 查看当前jupyter notebook的环境&#xff1a; 从上面可看出当前jupyter只有一个python 3的环境。 2、 在终端中切换至想要添加的环境&#xff1a; 2.1 激活你要添加的环境 conda activate sf 2.2 在当前环境中安装ipykernel conda install ipyke…

手把手教你【如何使用Vue3+Spring Boot实现一个视频点播功能】

一、简介 本项目是一个实际的视频点播应用&#xff0c;采用了Vue3和Spring Boot作为主要技术栈。旨在帮助开发者通过学习和参考实现思路来掌握相关知识。它主要解决了阿里云视频点播服务的接入、视频基础信息管理以及上传视频后获取视频ID等关键流程&#xff0c;涉及前后端交互…

【最新支持】OpenCV实验大师C++ SDK支持YOLOv10了推理了

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; OpenCV实验大师C SDK YOLO系列模型推理SDK 支持 YOLOv5、YOLOv8、YOLOv10系列网络从推理与部署 基于OpenCV DNN 与OpenVINO实…

Echarts 在指定部分做文字标记

文章目录 需求分析1. demo12. demo22. demo3 定位解决需求 实现在Echarts的折线图中,相同Y值的两点之间显示’abc’ 分析 1. demo1 使用 ECharts 的 markLine 功能来在相邻两个点之间添加标记。其中,我们通过设置标记的 yAxis 和 label 来控制标记的位置和显示内容。最后…

前端 CSS 经典:3D Hover Effect 效果

前言&#xff1a;有趣的 3D Hover Effect 效果&#xff0c;通过 js 监听鼠标移动&#xff0c;动态赋值 rotateX&#xff0c;rotateY 的旋转度来实现。 效果图&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta …

【NoSQL数据库】Redis简介

Redis Redis简介 Redis关系型数据库和非关系型数据库Redis 简介redis速度快的原因 Redis 配置Linux 源码安装redis命令工具 关系型数据库和非关系型数据库 关系型数据库&#xff08;Relational Database&#xff09;和非关系型数据库&#xff08;Non-Relational Database&…

在仓库新建分支之后,Vscode里面看不到

问题描述 在仓库新建了分支 但是在Vscode里面看不到这个新建的分支 解决 参考文章&#xff1a;http://t.csdnimg.cn/V92a3 在终端输入&#xff1a;git remote update origin --prune 命令解释 git remote update origin --prune 是一个 Git 命令&#xff0c;用于更新远程…

小猪APP分发:让APP封装变得如此简单

你是否曾经在开发完一款APP后&#xff0c;为了封装、分发而头疼不已&#xff1f;别担心&#xff0c;小猪APP分发来拯救你了&#xff01;这款神器不仅能让你的工作变得更加高效&#xff0c;还能让你的APP在各大平台上顺利分发。 小猪APP封装www.ppzhu.net APP封装的挑战 开发一…

Python3.9及以上Pyinstaller 反编译教程(exe转py)

文章目录 前言1.使用pyinstxtractor.py将exe文件转换成pyc文件2.给pyc文件添加文件头3.使用pycdc工具反编译pyc文件&#xff0c;获得源码 前言 经常使用pyinstaller将一些写的python程序打包成了各种exe&#xff0c;时间一长&#xff0c;源码丢失&#xff0c;为了恢复一部分源…

学习Python我能做些什么了?你真的了解了嘛?

工欲善其事&#xff0c;必先利其器。学习不是盲目的&#xff0c;是有目标性的。所以&#xff0c;在学习之前充分了解自己所学技能的前景&#xff0c;学完能做什么&#xff0c;大概地薪资待遇是很有必要的。 Python作为人工智能的重要编程语言&#xff0c;无论发展前景还是就业…

【文末附gpt升级秘笈】Suno全新功能在音乐创作领域的应用与影响

Suno全新功能在音乐创作领域的应用与影响 摘要&#xff1a; 随着科技的飞速发展&#xff0c;人工智能与音乐创作的结合日益紧密。本文旨在探讨Suno全新功能——即兴哼唱创作与声音模仿——在音乐创作领域的应用与影响。通过深入分析这一技术的原理、特点及其在音乐创作中的实际…

Linux 系统怎么快速「批量重命名」文件

如果需要对文件批量重命名&#xff0c;怎么办&#xff0c;是不是要找个工具&#xff0c;下载看这么使用。其实在 Linux、macOS 系统上使用脚本可以轻松搞定。 如&#xff0c;这里有一批图片文件&#xff0c;后缀名可能是jpg、jpeg、png 等&#xff0c;名称如 “我是待重命名的…

uniPush2.0消息推行(云对象)

1.创建uniCloud云开发环境 关联云服务空间&#xff08;没有云空间到官网上创建&#xff09;步骤如下 2. index.obj.js代码 &#xff0c;然后上传部署 // 云对象教程: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj // jsdoc语法提示教程&#xff1a;https://ask.dc…

6.6高算力开发套件深度解读 | 2024高通边缘智能创新应用大赛公开课

2024高通边缘智能创新应用大赛系列公开课即将迎来激动人心的收官之作&#xff01; 美格智能新技术研究院李书杰将在本次直播中带来一场关于边缘智能模组与开发技术的专业分享盛宴&#xff0c;深入剖析高算力开发套件的独特魅力和核心亮点。 锁定6月6日晚上8点&#xff0c;直播…