巧用SpringBoot优雅解决分布式限流
2020-12-13 06:20
标签:完成 get text lang ati 接口 没有 rgs pos 本篇从? 单机版中我们了解到 AtomicInteger、RateLimiter、Semaphore 这几种解决方案,但它们也仅仅是单机的解决手段,在集群环境下就透心凉了,后面又讲述了 Nginx 的限流手段,可它又属于网关层面的策略之一,并不能解决所有问题。例如供短信接口,你无法保证消费方是否会做好限流控制,所以自己在应用层实现限流还是很有必要的。 利用? 很简单… 在? 在? 创建一个? 默认情况下? 熟悉 Redis 的朋友都知道它是线程安全的,我们利用它的特性可以实现分布式锁、分布式限流等组件,在Spring Boot整合分布式锁中讲述了分布式锁的实现,限流相比它稍微复杂一点,官方虽然没有提供相应的API,但却提供了支持 Lua 脚本的功能,我们可以通过编写 Lua 脚本实现自己的API,同时他是满足原子性的…. 下面核心就是调用? 在接口上添加? 就一个普通的不能在普通的主函数类了 完成准备事项后,启动? 未达设定的阀值时 正确响应 达到设置的阀值时 错误响应 巧用SpringBoot优雅解决分布式限流 标签:完成 get text lang ati 接口 没有 rgs pos 原文地址:https://blog.51cto.com/14230003/2419614SpringBoot
?是为了简化?Spring
?应用的创建、运行、调试、部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程Spring Boot
、Redis
?应用层面来实现分布式的限流….分布式限流
本章目标
自定义注解
、Spring Aop
、Redis Cache
?实现分布式限流….具体代码
导入依赖
pom.xml
?中添加上?starter-web
、starter-aop
、starter-data-redis
?的依赖即可,习惯了使用?commons-lang3
?和?guava
?中的一些工具包…属性配置
application.properites
?资源文件中添加?redis
?相关的配置项spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=battcn
Limit 注解
Limit
?注解,不多说注释都给各位写齐全了….package com.battcn.limiter.annotation;
import com.battcn.limiter.LimitType;
import java.lang.annotation.*;
/**
* 限流
*
* @author Levin
* @since 2018-02-05
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Limit {
/**
* 资源的名字
*
* @return String
*/
String name() default "";
/**
* 资源的key
*
* @return String
*/
String key() default "";
/**
* Key的prefix
*
* @return String
*/
String prefix() default "";
/**
* 给定的时间段
* 单位秒
*
* @return int
*/
int period();
/**
* 最多的访问限制次数
*
* @return int
*/
int count();
/**
* 类型
*
* @return LimitType
*/
LimitType limitType() default LimitType.CUSTOMER;
}
public enum LimitType {
/**
* 自定义key
*/
CUSTOMER,
/**
* 根据请求者IP
*/
IP;
}
RedisTemplate
spring-boot-data-redis
?为我们提供了StringRedisTemplate
?但是满足不了其它类型的转换,所以还是得自己去定义其它类型的模板….package com.battcn.limiter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
/**
* @author Levin
* @since 2018/8/2 0002
*/
@Configuration
public class RedisLimiterHelper {
@Bean
public RedisTemplate
Limit 拦截器(AOP)
execute
?方法传入我们的 Lua 脚本内容,然后通过返回值判断是否超出我们预期的范围,超出则给出错误提示。package com.battcn.limiter;
import com.battcn.limiter.annotation.Limit;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Method;
/**
* @author Levin
* @since 2018/2/5 0005
*/
@Aspect
@Configuration
public class LimitInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LimitInterceptor.class);
private final RedisTemplate
控制层
@Limit()
?注解,如下代码会在 Redis 中生成过期时间为 100s 的 key = test 的记录,特意定义了一个?AtomicInteger
?用作测试…package com.battcn.controller;
import com.battcn.limiter.annotation.Limit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Levin
* @since 2018/8/2 0002
*/
@RestController
public class LimiterController {
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
@Limit(key = "test", period = 100, count = 10)
@GetMapping("/test")
public int testLimiter() {
// 意味著 100S 内最多允許訪問10次
return ATOMIC_INTEGER.incrementAndGet();
}
}
主函数
package com.battcn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Levin
*/
@SpringBootApplication
public class Chapter27Application {
public static void main(String[] args) {
SpringApplication.run(Chapter27Application.class, args);
}
}
测试
Chapter27Application
?自行测试即可,测试手段相信大伙都不陌生了,如?浏览器
、postman
、junit
、swagger
,此处基于?postman
,如果你觉得自带的异常信息不够友好,那么配上巧用SpringBoot轻松搞定全局异常?可以轻松搞定…
上一篇:【算法】计算质数
下一篇:01 如何学习易语言