服务限流之redis实现

项目中有些场景可能需要服务限流,服务限流有多种实现方式,本文将做一个简单示例,采用计数器的方式,通过redis实现。

需求举例:针对某个商户,限制一分钟内访问平台服务不超过10次。

主要思路: 基于redis做一个计数器,可以以商户编号为key,初始值为0,设置失效时间为一分钟。该商户每访问服务一次,计数加1,如果不超过10,允许继续访问,超过10,则直接返回拒绝访问提示。当超过一分钟时,原有的设置的键值失效,相当于计数器重置,又开始新的一分钟监控。

实现过程:

Springboot整合redis详细教程网上有很多很好的文章,本文就不做详细介绍,下面主要贴出一些关键代码。

准备一个redis工具类,名为为RedisUtils

package com.read.redis.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }
}

写一个示例接口:

controller层:

import com.read.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping(path = "/redis")
public class RedisController {

    @Autowired
    RedisService redisService;

    @RequestMapping("/requestTest")
    public String requestTest(HttpServletRequest request) {
        return redisService.requestTest(request);
    }
}

service层:

import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;

@Component
public interface RedisService {

    public String requestTest(HttpServletRequest request);
}

实现类:

import com.read.redis.service.RedisService;
import com.read.redis.util.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    RedisUtils redisUtils;

    public String requestTest(HttpServletRequest request) {
        String mchId = request.getParameter("mchid");   //商户编号
        if (redisUtils.hasKey(mchId)) {
            long ops = redisUtils.incr(mchId, 1);
            if (ops > 10) {
                return "一分钟内访问了" + ops + "次,超过了10次,访问被拒绝";
            } else {
                return "一分钟内访问了" + ops + "次,未超过10次,访问成功";
            }
        } else {
            redisUtils.set(mchId, 0);
            redisUtils.expire(mchId, 60);
            redisUtils.incr(mchId, 1);
            return "一分钟内访问了1次,未超过10次,访问成功";
        }
    }
}

可以手写一个客户端(很简单,不再详述)模拟服务调用,下面是测试的结果:

通过测试结果,发现达到了预期。本文只是一个简单的举例,针对具体场景可以灵活的拓展,不足之处敬请斧正。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值