redis令牌桶限流

常见的限流算法:

  • 漏桶算法:能够强行限制数据的传输速率,但是由于流速是恒定的,对突发特性的流量是无法处理的 (nginx限流使用的是漏桶算法,借助limit_conn_zone模块进行限流)
  • 令牌桶算法 :能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。
  • 滑动窗口算法:它本质上是计数算法,每个时间片段都有独立的计数器,通过这些计数器来限制请求的涌入。

总的来说:

  1. 令牌桶算法:能够保证自身系统的流量均匀;
  2. 漏桶算法:保证被调用系统(目标系统)流量均匀。

实际的生产中通常采用 漏桶算法+令牌桶算法的方式来对网络流量进行高效地控制。详细的限流算法介绍可参考我的博客:限流算法

Redis使用令牌桶方式进行限流

自定义接口:AccessLimit

@Inherited
@Documented
@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {


    int limit() default 5; //限制的次数


    int sec() default 5;//限制的时间段(秒)
}

配置拦截器:AccessLimitConfig

public class AccessLimitConfig implements WebMvcConfigurer {

    @Bean
    public AccessLimitInterceptor accessLimitInterceptor(){
        return new AccessLimitInterceptor();
    }


    /**
     * 配置拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessLimitInterceptor()).addPathPatterns("/**").excludePathPatterns("/attendance/userinfo/loginValidateCode","/swagger-ui.html","/swagger-resources/configuration/security",
                "/swagger-resources/configuration/ui","/swagger*//**","/api/v1/login","/v2/api-docs",
               "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**");
}

限流拦截器:AccessLimitInterceptor

public class AccessLimitInterceptor implements HandlerInterceptor {

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            if (!method.isAnnotationPresent(AccessLimit.class)) {
                return true;
            }
            AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
            if (accessLimit == null) {
                return true;
            }
            int limit = accessLimit.limit();
            int sec = accessLimit.sec();
            String key = CommonUtil.getIpAddr(request) + request.getRequestURI();
            Integer maxLimit = null;
            if(redisTemplate.opsForValue().get(key)!=null){
                maxLimit =Integer.valueOf(redisTemplate.opsForValue().get(key).toString()) ;
            }

            if (maxLimit == null) {
                redisTemplate.opsForValue().set(key, "1", sec, TimeUnit.SECONDS);  //set时一定要加过期时间
            } else if (maxLimit < limit) {
                redisTemplate.opsForValue().set(key, (maxLimit + 1)+"", sec, TimeUnit.SECONDS);
            } else {
                output(response, "请求太频繁!");
                return false;
            }
        }
        return true;
    }

    public void output(HttpServletResponse response, String msg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            outputStream.write(msg.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            outputStream.flush();
            outputStream.close();
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

接口使用

/**
* 这是用户模块的Controller
*/
@RestController
@RequestMapping("/attendance/commodity")
@Api(value = "商品模块")
public class CommodityController {

    @Autowired
    CommodityService commodityService;


    @Autowired
    RabbitTemplate rabbitTemplate;


    @Autowired
    RedisTemplate redisTemplate;


    @Autowired
    private Environment env;

    //日志记录器
    private static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

    /**
     * 进行商品秒杀
     */
    @PostMapping("/seckill")
    @ApiOperation(value = "进行商品秒杀")
    @AccessLimit(limit = 4,sec = 10)  //加上自定义注解即可
    public ResponseResult seckill(HttpServletRequest request) {
        logger.info("start UserController.seckill");
        String productId ="43486796d2871dfbec0f62951750ea4c";
        String user ="admin";
        OrderPojo orderPojo =new OrderPojo();
        orderPojo.setUserId(user);
        orderPojo.setProductId(productId);
        orderPojo.setOrderPrice(100L);
        orderPojo.setTempId(CommonUtil.getUUID());
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        rabbitTemplate.setExchange(env.getProperty("order.exchange.name"));
        rabbitTemplate.setRoutingKey(env.getProperty("order.routing.key.name"));
        try {
            Message message = MessageBuilder.withBody(JacksonUtils.toJson(orderPojo).getBytes("UTF-8")).build();
            //设置请求编码格式
            message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME, MessageProperties.CONTENT_TYPE_JSON);
            rabbitTemplate.convertAndSend(message);
        }catch (Exception e){
            logger.error("订单消息生产者发送消息失败"+e.getMessage(),e);
            return ResponseResult.error(ConstantsUtil.OPERATE_ERROR);
        }
        logger.info("end UserController.seckill");
        return ResponseResult.success(orderPojo.getTempId(),ConstantsUtil.OPERATE_SUCCESS);
    }

}

验证

在这里插入图片描述
在这里插入图片描述

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Redis令牌桶限流是一种常见的限流算法,它基于令牌桶算法实现。根据引用\[1\]中的代码,可以看出Redis令牌桶限流需要一个ScheduledThread来定时放入令牌。具体实现是通过使用ScheduledThreadExecutor.scheduleAtFixedRate方法,在规定的时间间隔内放入令牌到令牌桶中。 与漏桶算法相比,令牌桶算法允许一定程度的突发情况,同时可以方便地改变速率。引用\[2\]中提到,令牌桶算法可以根据需要提高放入桶中令牌的速率,从而提高限流速度。因此,令牌桶算法限流框架中的核心算法。 在使用Redis令牌桶限流时,可以通过调用相关方法来获取令牌。引用\[3\]中的代码示例展示了如何使用Redis令牌桶限流来限制上传操作的频率。在该示例中,通过调用redisRaterLimiter.acquireToken方法来获取令牌,如果获取到令牌则可以进行上传操作,否则会抛出限流异常。 综上所述,Redis令牌桶限流是一种基于令牌桶算法实现的限流方法,它可以通过定时放入令牌来控制请求的频率,并允许一定程度的突发情况。 #### 引用[.reference_title] - *1* *2* *3* [分布式限流实战--redis实现令牌桶限流](https://blog.csdn.net/u011296165/article/details/107761489)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值