首页 > 数据库 >Sentinel 的QPS限流和Redis流量风控

Sentinel 的QPS限流和Redis流量风控

时间:2024-03-29 19:01:20浏览次数:31  
标签:访问 Redis 风控 lua 限流 new response public

目录

Sentinel 的QPS限流

导入依赖:

这个限制的时每秒的QPS为1:

如果触发风控,设置降级策略。

在代码中引入 Sentinel 注解控制流控规则。

用Redis实现流量风控:

lua脚本如下:

配置过滤器器执行lua脚本,判断访问次数是否超过:我们可以通过这个过滤器得知,再timeWindow时间内,我们的访问次数是否超过设定的currentAccessCount

然后配置过滤器使其生效:

下面是针对上面的一些参数的配置读取

yaml里面配置风控的参数


Sentinel 的QPS限流
导入依赖:

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

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
        </dependency>
这个限制的时每秒的QPS为1:
/**
 * 初始化限流配置
 * 
 */
@Component
public class SentinelRuleConfig implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule createOrderRule = new FlowRule();
        createOrderRule.setResource("create_short-link");
        createOrderRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        createOrderRule.setCount(1);
        rules.add(createOrderRule);
        FlowRuleManager.loadRules(rules);
    }
}
如果触发风控,设置降级策略。
/**
 * 自定义流控策略
 * 
 */
public class CustomBlockHandler {

    public static Result<ShortLinkCreateRespDTO> createShortLinkBlockHandlerMethod(ShortLinkCreateReqDTO requestParam, BlockException exception) {
        return new Result<ShortLinkCreateRespDTO>().setCode("B100000").setMessage("当前访问网站人数过多,请稍后再试...");
    }
}
在代码中引入 Sentinel 注解控制流控规则。
/**
 * 创建XXX
 */
@PostMapping("/api/short-link/v1/create")
@SentinelResource(
        value = "create_short-link",
        blockHandler = "createShortLinkBlockHandlerMethod",
        blockHandlerClass = CustomBlockHandler.class
)
public Result<ShortLinkCreateRespDTO> createShortLink(@RequestBody ShortLinkCreateReqDTO requestParam) {
    return Results.success(shortLinkService.createShortLink(requestParam));
}

限制了每秒钟只能有一个创建XXX的请求被成功处理。多余的请求在该时间段内会被限流处理(如返回错误信息、等待、丢弃等),直到下一秒开始重新计数。这样可以防止短时间内大量请求对服务端造成压力,保证系统的稳定性和可用性。


用Redis实现流量风控:

用redis的自增,key不存在,设置值为1,每次访问加1。

有效期是timeWindow,访问次数是currentAccessCount

lua脚本如下:
-- 设置用户访问频率限制的参数
local username = KEYS[1]
local timeWindow = tonumber(ARGV[1]) -- 时间窗口,单位:秒

-- 构造 Redis 中存储用户访问次数的键名
local accessKey = "short-link:user-flow-risk-control:" .. username

-- 原子递增访问次数,并获取递增后的值
local currentAccessCount = redis.call("INCR", accessKey)

-- 设置键的过期时间
redis.call("EXPIRE", accessKey, timeWindow)

-- 返回当前访问次数
return currentAccessCount
配置过滤器器执行lua脚本,判断访问次数是否超过:
我们可以通过这个过滤器得知,再timeWindow时间内,我们的访问次数是否超过设定的currentAccessCount
/**
 *  用户操作流量分控过滤器
 */
@Slf4j
@RequiredArgsConstructor
public class UserFlowRiskControlFilter implements Filter {

    private final StringRedisTemplate stringRedisTemplate;
    private final UserFlowRiskControlConfiguration userFlowRiskControlConfiguration;

    private static final String USER_FLOW_RISK_CONTROL_LUA_SCRIPT_PATH = "lua/user_flow_risk_control.lua";

    @SneakyThrows
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        // lua脚本执行 魔板
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        //lua脚本执 路径
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(USER_FLOW_RISK_CONTROL_LUA_SCRIPT_PATH)));
        //返回值结果类型
        redisScript.setResultType(Long.class);
        String username = Optional.ofNullable(UserContext.getUsername()).orElse("other");
        Long result;
        try {
            result = stringRedisTemplate.execute(redisScript, Lists.newArrayList(username), userFlowRiskControlConfiguration.getTimeWindow());
        } catch (Throwable ex) {
            log.error("执行用户请求流量限制LUA脚本出错", ex);
            returnJson((HttpServletResponse) response, JSON.toJSONString(Results.failure(new ClientException(FLOW_LIMIT_ERROR))));
            return;
        }
        if (result == null || result > userFlowRiskControlConfiguration.getMaxAccessCount()) {
            returnJson((HttpServletResponse) response, JSON.toJSONString(Results.failure(new ClientException(FLOW_LIMIT_ERROR))));
            return;
        }
        filterChain.doFilter(request, response);
    }

    private void returnJson(HttpServletResponse response, String json) throws Exception {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        try (PrintWriter writer = response.getWriter()) {
            writer.print(json);
        }
    }
}
然后配置过滤器使其生效:
package com.wyk.config;


import com.wyk.common.biz.user.UserFlowRiskControlFilter;
import com.wyk.common.biz.user.UserTransmitFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * 用户配置自动装配
 */
@Configuration
public class UserConfiguration {

    /**
     * 用户操作流量风控过滤器
     */
    @Bean
    @ConditionalOnProperty(name = "short-link.flow-limit.enable", havingValue = "true")
    public FilterRegistrationBean<UserFlowRiskControlFilter> globalUserFlowRiskControlFilter(
            StringRedisTemplate stringRedisTemplate,
            UserFlowRiskControlConfiguration userFlowRiskControlConfiguration) {
        FilterRegistrationBean<UserFlowRiskControlFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new UserFlowRiskControlFilter(stringRedisTemplate, userFlowRiskControlConfiguration));
        registration.addUrlPatterns("/*");
        registration.setOrder(10);
        return registration;
    }
}
下面是针对上面的一些参数的配置读取
​
@Data
@Component
@ConfigurationProperties(prefix = "short-link.flow-limit")
public class UserFlowRiskControlConfiguration {

    /**
     * 是否开启用户流量风控验证
     */
    private Boolean enable;

    /**
     * 流量风控时间窗口,单位:秒
     */
    private String timeWindow;

    /**
     * 流量风控时间窗口内可访问次数
     */
    private Long maxAccessCount;
}

​
yaml里面配置风控的参数
short-link:
  flow-limit:
    enable: true
    time-window: 1
    max-access-count: 20

标签:访问,Redis,风控,lua,限流,new,response,public
From: https://blog.csdn.net/m0_66532138/article/details/137153163

相关文章

  • Redis Map数据结构中相同key不同的字段会分散多节点存储吗?
    目录结论说明 结论   无论是单实例Redis还是Redis集群,一个Map数据类型的key对应的所有字段和值都存储在同一台机器上。在Redis集群中,这是通过哈希槽机制来保证的,确保了对同一个key的操作不需要跨节点通信,从而提高了操作的效率。说明    Redis的Map数据类......
  • 美团一面:项目中使用过Redis吗?我说用Redis做缓存。他对我哦了一声
    引言Redis,作为一种开源的、基于内存且支持持久化的键值存储系统,以其卓越的性能、丰富灵活的数据结构和高度可扩展性在全球范围内广受欢迎。Redis不仅提供了一种简单直观的方式来存储和检索数据,更因其支持数据结构如字符串、哈希、列表、集合、有序集合等多种类型,使得其在众多场景......
  • 【Redis】Redis的数据类型速查(5种基础类型,5特殊类型)
    Redis的数据类型如下类型名称所属类型说明应用场景string字符串基础数据类型字符串类型,最大存储空间不超过512m,详细可以分为3类:string:普通字符串int:整数类型,可以做自增、自减操作float:浮点类型,可以做自增、自减操作计数器hash哈希基础数据类型也......
  • 什么是redis中的哈希桶、哈希冲突及解决方法
    什么是哈希桶Redis中的哈希桶是一种数据结构,用于在Redis的哈希表(如字典结构)中存储键值对。哈希桶是哈希表数组中的每个元素,可以视为一个容器或槽位,用于存放数据。在Redis中,当插入一个新的键值对时,会根据键的哈希值计算出一个索引,该索引指向特定的哈希桶。每个哈希桶可以存储多个......
  • Redis五大数据类型常用命令
    Redis的五大数据类型1.redis键(key)​1)查看当前库的所有键127.0.0.1:6379>keys*​2)判断某个键是否存在127.0.0.1:6379>exists<key>例如:127.0.0.1:6379>existst1​3)查看键对应的value的类型127.0.0.1:6379>type<key>​4)删除某个键127.0.0.1:6379......
  • redis自学(25)过期策略
    Redis内存回收Redsi之所以性能强,最主要的原因就是基于内存存储。然而但决断的redis其内存大小不宜过大,会影响持久化或者主从同步性。我们可以通过修改配置文件来设置redis的最大内存:  当内存使用达到上限时,就无法存储更多数据了过期策略在学习redis缓存的时候我们说过,可......
  • 【Redis】redis哨兵模式
    概述RedisSentinel,即Redis哨兵,在Redis2.8版本开始引入。它是Redis高可用的实现方案之一。Sentinel是一个管理多个Redis实例的工具,它的核心功能是可以实现对Redis的监控、通知、自动故障转移。监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。自动故障转移(Au......
  • etcd与redis之间的区别
    一、简介我们之前用了redis,那么好用为什么还要来用etcd呢,这里就来和大家聊聊为什么有的业务场景选择etcd。分析:在当今的分布式系统中,数据存储及一致性相当重要。etcd和redis都是我们最受欢迎的开源分布式数据存储的解决方案,但是他们有着不同的试用场景。下面我个人对其中二个的......
  • 怎样去保证 Redis 缓存与数据库双写一致性?
    解决方案那么我们这里列出来所有策略,并且讨论他们优劣性。先更新数据库,后更新缓存先更新数据库,后删除缓存先更新缓存,后更新数据库先删除缓存,后更新数据库先更新数据库,后更新缓存    这种方法是不推荐使用的,因为在更新缓存那一步有的业务需求缓存中的值并不是从数据......
  • 如何实现Redis集群的高可用性
    在实际应用中,确保Redis集群的高可用性是至关重要的。以下是一些常见的实现高可用性的方法和相关代码示例。1、主从复制(Master-SlaveReplication):原理:主节点负责处理数据写入操作,而从节点则从主节点复制数据。这样,即使主节点发生故障,从节点可以升级为主节点,继续提供服务......