java 限流器实现

一、目的

   并发问题处理:单位时间内请求次数过多,访问量较大时,报错提示用户。往往需要进行限流(每一秒限制请求几次)

注意: 这边说的访问量较大,是指有修改数据库数据; 正常的纯查询, 无需做限流; 当然,要做也是可以的

二、基本思路

   使用 redis 缓存加 scheduler 定时器进行实现;

   以每一小时为处理的时间单位,以每一秒为 job 的启动间隔;

   在一小时内,为每一秒都分配一定的令牌数量(即限流的次数);

   在每一秒内,请求了需限流的接口,则将令牌数量减 1 ;在这一秒内,一旦在请求时发现令牌数量为 0 时,则直接报错提示

三、具体实现

   1、使用定时器分配令牌数量:

	@Override
    protected void execute() throws Throwable {
        logger.info("begin to run FreeProductTokenJob");
        // 1 个小时,每间隔 1 秒钟,发放指定数量的Token
        int maxTime = 60 * 60 * 1;
        for (int i = 0; i < maxTime; i++) {
            sleep(1000);
            redisAccess.stringOps().set(RedisKeyConstant.FREE_PRODUCT_TOKEN_KEY, String.valueOf(tokenCount));
        }
        redisAccess.keyOps().delete(RedisKeyConstant.FREE_PRODUCT_TOKEN_KEY);
        logger.info("End to run FreeProductTokenJob");
    }

    private void sleep(int millisecond) {
        try {
            Thread.sleep(millisecond);
        } catch (InterruptedException e) {
            logger.error("Sleep error", e);
        }
    }

   2、controller、service 消费令牌:

	@Inject
    private FreeProductTokenService freeProductTokenService;

    @Override
    public FreeProductGetTokenResponse getToken() {
        FreeProductGetTokenResponse response = new FreeProductGetTokenResponse();
        response.setCanRunInto(freeProductTokenService.getToken());
        return super.setSuccessBaseResponse(response, "");
    }
	@Service
	public class FreeProductTokenService {
	    @Inject
	    private RedisAccess redisAccess;
	
	    public boolean getToken() {
	        if (!redisAccess.keyOps().exists(RedisKeyConstant.FREE_PRODUCT_TOKEN_KEY)) {
	            return true;
	        }
	        String count = redisAccess.stringOps().get(RedisKeyConstant.FREE_PRODUCT_TOKEN_KEY);
	        if (StringUtils.hasText(count) && Integer.parseInt(count) > 0) {
	        	// 调用一次,令牌数量减 1
	            redisAccess.stringOps().increment(RedisKeyConstant.FREE_PRODUCT_TOKEN_KEY, -1);
	            
	            return true;
	        }
	        return false;
	    }
	}

   3、在需要限流的接口处调用:

	if (!productFavoriteWrapper.getToken()) {
	    setErrorResponse(response, 180007, "当前购买人数过多,请稍后尝试");
	    return response;
	}
	@Inject
    private FreeProductServiceAPI freeProductServiceAPI;

    public boolean getToken() {
        FreeProductGetTokenResponse response = freeProductServiceAPI.getToken();
        if (null == response || 200 != response.getCode()) {
            return false;
        }
        return response.isCanRunInto();

    }
发布了54 篇原创文章 · 获赞 19 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41970025/article/details/101102060