接口限流实例

注解

package com.imooc.miaosha.access;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
//seconds秒内最多只能访问maxCount次,并且接口要登录才能访问
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
	int seconds();
	int maxCount();
	boolean needLogin() default true;
}
package com.imooc.miaosha.access;

import com.imooc.miaosha.domain.MiaoshaUser;

public class UserContext {
	
	private static ThreadLocal<MiaoshaUser> userHolder = new ThreadLocal<MiaoshaUser>();
	
	public static void setUser(MiaoshaUser user) {
		userHolder.set(user);
	}
	
	public static MiaoshaUser getUser() {
		return userHolder.get();
	}

}

package com.imooc.miaosha.access;

import java.io.OutputStream;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.alibaba.fastjson.JSON;
import com.imooc.miaosha.domain.MiaoshaUser;
import com.imooc.miaosha.redis.AccessKey;
import com.imooc.miaosha.redis.RedisService;
import com.imooc.miaosha.result.CodeMsg;
import com.imooc.miaosha.result.Result;
import com.imooc.miaosha.service.MiaoshaUserService;

@Service
public class AccessInterceptor  extends HandlerInterceptorAdapter{
	
	@Autowired
	MiaoshaUserService userService;
	
	@Autowired
	RedisService redisService;
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		if(handler instanceof HandlerMethod) {
			MiaoshaUser user = getUser(request, response);
			UserContext.setUser(user);
			HandlerMethod hm = (HandlerMethod)handler;
			AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
			if(accessLimit == null) {
				return true;
			}
			int seconds = accessLimit.seconds();
			int maxCount = accessLimit.maxCount();
			boolean needLogin = accessLimit.needLogin();
			String key = request.getRequestURI();
			if(needLogin) {
				if(user == null) {
					render(response, CodeMsg.SESSION_ERROR);
					return false;
				}
				key += "_" + user.getId();
			}else {
				//do nothing
			}
			AccessKey ak = AccessKey.withExpire(seconds);
			Integer count = redisService.get(ak, key, Integer.class);
	    	if(count  == null) {
	    		 redisService.set(ak, key, 1);
	    	}else if(count < maxCount) {
	    		 redisService.incr(ak, key);
	    	}else {
	    		render(response, CodeMsg.ACCESS_LIMIT_REACHED);
	    		return false;
	    	}
		}
		return true;
	}
	
	private void render(HttpServletResponse response, CodeMsg cm)throws Exception {
		response.setContentType("application/json;charset=UTF-8");
		OutputStream out = response.getOutputStream();
		String str  = JSON.toJSONString(Result.error(cm));
		out.write(str.getBytes("UTF-8"));
		out.flush();
		out.close();
	}

	private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response) {
		String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
		String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
		if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
			return null;
		}
		String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
		return userService.getByToken(response, token);
	}
	
	private String getCookieValue(HttpServletRequest request, String cookiName) {
		Cookie[]  cookies = request.getCookies();
		if(cookies == null || cookies.length <= 0){
			return null;
		}
		for(Cookie cookie : cookies) {
			if(cookie.getName().equals(cookiName)) {
				return cookie.getValue();
			}
		}
		return null;
	}
	
}

### Spring Boot 中实现接口限流的方法 #### 使用 Bucket4j 库进行限流 Bucket4j 是一个 Java 实现的高性能限流库,支持多种限流算法,如令牌桶算法。通过使用 Bucket4j 可以轻松地在 Spring Boot 应用中实现复杂的限流逻辑,并且它还提供了丰富的配置选项和统计功能[^1]。 为了集成 Bucket4j 到 Spring Boot 项目中,首先需要引入依赖: ```xml <dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-spring-boot-starter</artifactId> <version>版本号</version> </dependency> ``` 接着可以在控制器方法中创建并配置限流实例: ```java import io.github.bucket4j.Bucket; import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Refill; // 创建带宽限制策略 Bandwidth limit = Bandwidth.classic(10, Refill.greedy(5, Duration.ofMinutes(1))); Bucket bucket = Bucket.builder().addLimit(limit).build(); if (!bucket.tryConsume(1)) { throw new TooManyRequestsException("Too many requests"); } ``` #### 基于 AOP 的限流实现 另一种常见的做法是在面向切面编程 (AOP) 中实现限流机制。具体来说,可以通过环绕通知的方式,在执行目标方法之前解析其上的限流注解,进而决定是否允许请求继续处理[^2]。 定义自定义注解 `@RateConfigAnno` 来标记哪些方法应该受到速率限制的影响: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateConfigAnno { String limitType(); // 限流类型 double limitCount() default 5d; // 默认每秒最大访问次数 } ``` 编写相应的 AOP 类用于拦截被此注解修饰的方法调用,并实施实际的流量控制逻辑[^3]: ```java @Aspect @Component public class RateLimiterAspect { private final Map<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>(); @Around("@annotation(rateConfig)") public Object applyRateLimits(final ProceedingJoinPoint joinPoint, final RateConfigAnno rateConfig) throws Throwable { // 获取当前方法签名作为 key String methodName = joinPoint.getSignature().toString(); synchronized (rateLimiters) { if (!rateLimiters.containsKey(methodName)) { initRateLimiter(methodName, rateConfig); } } try { return proceedIfAllowed(joinPoint, methodName); } catch (final Exception e) { log.error(e.getMessage(), e); throw e; } } private void initRateLimiter(String name, RateConfigAnno config){ switch(config.limitType()){ case "fixedWindow": rateLimiters.put(name, FixedWindowRateLimiter.create((int)config.limitCount())); break; // 更多类型的初始化... } } private Object proceedIfAllowed(ProceedingJoinPoint pjp, String methodKey) throws Throwable{ boolean allowed = rateLimiters.getOrDefault(methodKey, null).tryAcquirePermission(); if(!allowed){ throw new RuntimeException("Exceeded rate limits."); } return pjp.proceed(); } } ``` 以上两种方式均能有效地帮助开发者构建起一套完善的API限流保护措施,保障服务稳定性和用户体验的同时也提高了系统的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值