注解使用redisson完成api限流

  1. 注解类
package com.sanrex.annotation;

import org.redisson.api.RateIntervalUnit;

import java.lang.annotation.*;

/**
 * Redisson限流注解
 *
 * @author :chou
 * @date :2022-01-24 10:11
 **/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Limiter {

    /**
     * 限流器的 key
     *
     * @return key
     */
    //String key();

    /**
     * 限制数量
     *
     * @return 许可数量
     */
    long limitNum() default 20;

	/**
	 * 速率时间间隔
	 *
	 * @return 速率时间间隔
	 */
	long limitTimeInterval() default 1;

	/**
	 * 时间单位
	 *
	 * @return 时间
	 */
	RateIntervalUnit limitTime() default RateIntervalUnit.MINUTES;
}

  1. aop切面类
package com.sanrex.aop;

import com.sanrex.annotation.Limiter;
import com.sanrex.quote.util.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
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.lang.reflect.Method;

/**
 * Redisson限流注解
 *
 * @author :chou
 * @date :2022-01-24 10:11
 **/
@Component
@Aspect
@Slf4j
public class LimiterAspect {

	private RedissonClient redisson;

	public LimiterAspect(RedissonClient redissonClient) {
		this.redisson = redissonClient;
	}

	@Pointcut("@annotation(com.sanrex.annotation.Limiter)")
	public void limitAspect() {}

	@Around("limitAspect()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
		Limiter limiterAnnotation = method.getAnnotation(Limiter.class);
		long limitNum = limiterAnnotation.limitNum();
		long limitTimeInterval = limiterAnnotation.limitTimeInterval();

		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		String ip = IPUtils.getIpAddr(request);
		String key = "req:limit:" + ip + "-" + request.getRequestURI();

		RateIntervalUnit rateIntervalUnit = limiterAnnotation.limitTime();
		RRateLimiter limiter = redisson.getRateLimiter(key);
		// trySetRate方法只会执行一次,目前暂时没有提供方法重新设置速率,只能删除后重建 issue#2322
		limiter.trySetRate(RateType.OVERALL, limitNum, limitTimeInterval, rateIntervalUnit);
		boolean allow = limiter.tryAcquire();
		if (!allow) {
			String url = request.getRequestURL().toString();
			String unit = "";
			if (rateIntervalUnit == RateIntervalUnit.SECONDS) {
				unit = "秒";
			} else if (rateIntervalUnit == RateIntervalUnit.MINUTES) {
				unit = "分钟";
			} else if (rateIntervalUnit == RateIntervalUnit.HOURS) {
				unit = "小时";
			} else if (rateIntervalUnit == RateIntervalUnit.DAYS) {
				unit = "天";
			}
			String tooManyRequestMsg = String.format("用户IP[%s]访问地址[%s]时间间隔[%s %s]超过了限定的次数[%s]", ip, url, limitTimeInterval, unit, limitNum);
			log.info(tooManyRequestMsg);
			throw new RuntimeException(tooManyRequestMsg);
		}
		return joinPoint.proceed();
	}
}

  1. 实际使用
	@Limiter(limitNum = 5, limitTimeInterval = 3)
	@PostMapping("/product_unit")
	public R apiMqPullData(@RequestBody Map queryMap) {
		Integer pullCount = provider.apiMqPullData(queryMap);
		return R.success(String.format("拉取到数据到MQ数量: %s", pullCount));
	}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值