你的系统支持限流吗?

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 很多时候为了系统的安全性以及负载考虑,我们都会在请求量过高的时候对系统进行限流处理,那么今天我就我自己做过的一个系统里面的限流方案来介绍一下
很多时候为了系统的安全性以及负载考虑,我们都会在请求量过高的时候对系统进行限流处理,那么今天我就我自己做过的一个系统里面的限流方案来介绍一下。

1.首先定义一个注解,用于加在需要进行限流的方法上面对方法的请求进行限流

package cn.org.ppdxzz.limit;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.annotation.*;

/**
 * @description: 自定义限流注解
 * @author: PeiChen
 * @version: 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
    /**
     * 允许访问的最大次数
     */
    int count() default Integer.MAX_VALUE;

    /**
     * 时间段,单位是毫秒,默认值一分钟
     */
    long time() default 60000;
}

2.定义一个限流切面类,去处理限流相应的一些逻辑

package cn.org.ppdxzz.limit;

import cn.org.ppdxzz.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * @description: 限流切面处理类
 * @author: PeiChen
 * @version: 1.0
 */
@Slf4j
@Aspect
@Component
public class RequestLimitAspect {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Before("@annotation(limit)")
    public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {
        try {
            Object[] args = joinPoint.getArgs();
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            for (Object arg : args) {
                if (arg instanceof HttpServletRequest) {
                    request = (HttpServletRequest) arg;
                    break;
                }
            }
            String ip = IpUtils.getIpAddress(request);
            String url = request.getRequestURL().toString();
            String key = "limit_".concat(url).concat(ip);
            if (!redisTemplate.hasKey(key)) {
                redisTemplate.opsForValue().set(key,String.valueOf(1));
            } else {
                Integer getValue = Integer.parseInt(redisTemplate.opsForValue().get(key)) + 1;
                redisTemplate.opsForValue().set(key,String.valueOf(getValue));
            }
            redisTemplate.expire(key,limit.time(),TimeUnit.MILLISECONDS);
            int count = Integer.parseInt(redisTemplate.opsForValue().get(key));
            if (count == 1) {
                //创建一个定时器
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {
                        redisTemplate.delete(key);
                    }
                };
                //这个定时器设定在time规定的时间之后会执行上面的remove方法,也就是说在这个时间后它可以重新访问
                new Timer().schedule(timerTask, limit.time());
            }
            if (count > limit.count()) {
                log.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
                throw new RequestLimitException();
            }
        } catch (RequestLimitException e) {
            throw e;
        } catch (Exception e) {
            log.error("系统异常,限流中...", e);
        }
    }
}

3.开发一个限流异常处理类,用于处理系统开始限流的请求

package cn.org.ppdxzz.limit;

/**
 * @description: 自定义限流异常类
 * @author: PeiChen
 * @version: 1.0
 */
public class RequestLimitException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public RequestLimitException() {
        super("接口请求过于频繁,请稍后再试");
    }
    public RequestLimitException(String message) {
        super(message);
    }

}

4.使用限流注解,直接在所需要限流的方法上面加上限流注解

@RequestLimit(count = 5)//代表一分钟最多请求5次,超出则限流
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
SpringCloudAlibaba 监控 Dubbo
SpringCloudAlibaba篇(三)整合Sentinel(限流、流量整形、熔断降级、系统负载保护、热点防护,分布式服务架构的高可用流量防护组件)
SpringCloudAlibaba篇(三)整合Sentinel(限流、流量整形、熔断降级、系统负载保护、热点防护,分布式服务架构的高可用流量防护组件)
SpringCloudAlibaba篇(三)整合Sentinel(限流、流量整形、熔断降级、系统负载保护、热点防护,分布式服务架构的高可用流量防护组件)
|
4月前
|
弹性计算
异步任务处理系统问题之哪些级别的系统支持任务流控能力
异步任务处理系统问题之哪些级别的系统支持任务流控能力
|
5月前
|
监控 算法 Java
高并发架构设计三大利器:缓存、限流和降级问题之配置Sentinel的流量控制规则问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之配置Sentinel的流量控制规则问题如何解决
|
5月前
|
算法 API 缓存
高并发架构设计三大利器:缓存、限流和降级问题之固定窗口限流算法的原理是什么
高并发架构设计三大利器:缓存、限流和降级问题之固定窗口限流算法的原理是什么
|
5月前
|
应用服务中间件 nginx 缓存
高并发架构设计三大利器:缓存、限流和降级问题之Nginx作为前置网关进行限流问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之Nginx作为前置网关进行限流问题如何解决
|
开发框架 算法 NoSQL
多租户系统中如何实现分别限流
多租户系统中如何实现分别限流
227 1
|
7月前
|
缓存 Java 应用服务中间件
常见的限流降级方案
【1月更文挑战第21天】
|
7月前
|
监控 测试技术 数据安全/隐私保护
如何集成Sentinel实现流控、降级、热点规则、授权规则总结
如何集成Sentinel实现流控、降级、热点规则、授权规则总结
275 0
|
存储 SpringCloudAlibaba 算法
系统高可用(一):限流
限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机
190 0
系统高可用(一):限流
|
算法 NoSQL Java
单机限流和分布式应用限流
单机限流和分布式应用限流
356 0

热门文章

最新文章