zuul实现限流

zuul限流

限流算法

  • 漏桶: leakey bucket,原理:桶的下方的小孔会以一个相对恒定的速率漏水,而不管入桶的水流量,这样就达到了控制出水口的流量
  • 令牌桶: token bucket,原理:以相对恒定的速率向桶中加入令牌,请求来时于桶中取令牌,取到了就放行,没能取到令牌的请求则丢弃

限流粒度

粗粒度
  • 网关限流
  • 单个服务
细粒度
  • user: 认证用户或者匿名,针对某个用户粒度进行限流
  • origin: 客户机的IP,针对请求客户机的IP进行限流
  • url: 特定url,针对请求的url粒度进行限流
  • serviceId: 特定服务,针对某个服务的id粒度进行限流

实现

从单个服务的角度实现限流,原理:利用redis键过期的自动删除的特性。以url为key,如果key不存在,创建key,并设置键过期时间,相同请求过来就对这个key进行计数,使用redis.incr原子方法,当请求超过limit时,则不让请求api。

1.定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {

    //每秒请求
    int value() default 10;

}

2.定义切面

@Aspect
@Component
@Slf4j
public class RateLimitAspect {


    @Autowired
    private RedisTemplate redisTemplate;

    @Pointcut(value = "@annotation(com.pinlor.gml.cloudrateservice.annotation.RateLimit)")
    public void pointcut(){

    }


    @Around(value = "pointcut()")
    @ResponseBody
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.debug("限速检测");
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RateLimit rate = method.getAnnotation(RateLimit.class);
        int limit = rate.value();
        HttpServletRequest request = RequestUtil.getRequest();
        String key = request.getRequestURI();
        //最好以下部分用redis分布锁加锁一下
        Object object = redisTemplate.opsForValue().get(key);
        if (object == null){
            redisTemplate.opsForValue().increment(key);
            redisTemplate.expire(key, 1, TimeUnit.SECONDS);
        }else {
            System.out.println("当前记数: "+ redisTemplate.opsForValue().get(key));
            if (redisTemplate.opsForValue().increment(key) > limit) {
                System.out.println("服务器繁忙,请稍后再试!!");
                return "服务器繁忙,请稍后再试!!";
            }
        }

        return joinPoint.proceed();

    }

}

3.redis配置:

@Configuration
public class RedisConfig {

   @Autowired
   private RedisConnectionFactory redisConnectionFactory;

    // 默认用的是用JdkSerializationRedisSerializer进行序列化的
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 注入数据源
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
        					 new Jackson2JsonRedisSerializer(Object.class);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // key-value结构序列化数据结构
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash数据结构序列化方式,必须这样否则存hash 就是基于jdk序列化的
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 启用默认序列化方式
        redisTemplate.setEnableDefaultSerializer(true);
        redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);

        /// redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

4.api服务:

@RestController
@RequestMapping(value = "/rc")
public class RateController {

    @Value(value = "${server.port}")
    private int port;

    @RateLimit(value = 10)
    @GetMapping("/getRate")
    public String getRate(){
        System.out.println("rate from: "+ port);
        return "rate from: "+ port;
    }

}

5.工具类:

public class RequestUtil {

    public static HttpServletRequest getRequest(){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null){
            return null;
        }
        return ((ServletRequestAttributes)requestAttributes).getRequest();
    }
}

6.maven依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值