2、api限流實戰

01SpringBoot中集成Redis

SpringBoot中集成Redis相對比較簡單,步驟如下:

1.1 引入Redis依賴

<!-- springboot redis依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

1.2 在application.yml中配置Redis

spring:
redis:
database: 3 # Redis數據庫索引(默認為0)
host: 127.0.0.1 # Redis服務器地址
port: 6379 # Redis服務器連接端口
password: 123456 # Redis服務器連接密碼(默認為空)
timeout: 2000 # 連接超時時間(毫秒)

jedis:
pool:
max-active: 200 # 連接池最大連接數(使用負值表示沒有限制)
max-idle: 20 # 連接池中的最大空閑連接
min-idle: 0 # 連接池中的最小空閑連接
max-wait: -1 # 連接池最大阻塞等待時間(使用負值表示沒有限制)

1.3 配置RedisTemplate

/**
* @Description: redis配置類
* @Author oyc
*/

@Configuration
@EnableCaching

public class RedisConfig extends CachingConfigurerSupport {

/**
* RedisTemplate相關配置
* 使redis支持插入對象
*
* @param factory
* @return 方法緩存 Methods the cache
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();

// 配置連接工廠
template.setConnectionFactory(factory);
// 設置key的序列化器
template.setKeySerializer(new StringRedisSerializer());
// 設置value的序列化器
// 使用Jackson 2,將對象序列化為JSON
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);

return template;
}
}

以上,已經完成Redis的集成,后續使用可以直接注入RedisTemplate,如下所示:

@Autowired
private RedisTemplate<String, Object> redisTemplate;

02實現api限流

2.1 添加自定義AccessLimit注解

使用注解方式實現接口的api限流操作,方便而優雅。

/**
* @Description:
* @Author oyc
*/

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

/**
* 指定second時間內API請求次數
*/
int maxCount() default 5;

/**
* 請求次數的指定時間范圍 秒數(redis數據過期時間)
*/
int second() default 60;
}

2.2 編寫攔截器

api限流的思路

/**
* @Description: 訪問攔截器
* @Author oyc
*/

@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try { // Handler 是否為 HandlerMethod 實例
if (handler instanceof HandlerMethod) {
// 強轉
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 獲取方法
Method method = handlerMethod.getMethod();
// 是否有AccessLimit注解
if (method.isAnnotationPresent(AccessLimit.class)) {
return true;
}
// 獲取注解內容信息
AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}
int seconds = accessLimit.second();
int maxCount = accessLimit.maxCount();

// 存儲key
String key = request.getRemoteAddr() + ":" + request.getContextPath() + ":" + request.getRequestURI();

// 已經訪問的次數
Integer count = (Integer) redisTemplate.opsForValue().get(key);
System.out.println("已經訪問的次數:" + count);
if (null == count || -1 == count) {
redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);
return true;
}

if (count < maxCount) {
redisTemplate.opsForValue().increment(key);
return true;
}

if (count >= maxCount) {
logger.warn("請求過于頻繁請稍后再試");
return false;
}
}
return true;
} catch (Exception e) {
logger.warn("請求過于頻繁請稍后再試");
e.printStackTrace();
return true;
}
}
}

2.3 注冊攔截器并配置攔截路徑和不攔截路徑

/**
* @Description: 訪問攔截器配置
* @Author oyc
* @Date 2020/10/22 11:34 下午
*/

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

@Autowired
private AccessLimitInterceptor accessLimitInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLimitInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/login.html", "/user/login");
}
}

2.4 使用AccessLimit

/**
* @Description:
* @Author oyc
*/

@RestController
@RequestMapping("access")
public class AccessLimitController {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

/**
* 限流測試
*/
@GetMapping
@AccessLimit(maxCount = 3, second = 60)
public String limit(HttpServletRequest request) {
logger.error("Access Limit Test");
return "限流測試";
}
}

2.5 測試

已經訪問的次數:null
2020-10-23 00:19:47.423 ERROR 1496 --- [nio-8080-exec-7] c.o.s.AccessLimitController : Access Limit Test
已經訪問的次數:1
2020-10-23 00:19:48.543 ERROR 1496 --- [nio-8080-exec-6] c.o.s.AccessLimitController : Access Limit Test
已經訪問的次數:2
2020-10-23 00:19:49.069 ERROR 1496 --- [nio-8080-exec-8] c.o.s.AccessLimitController : Access Limit Test
已經訪問的次數:3
2020-10-23 00:19:53.612 WARN 1496 --- [nio-8080-exec-9] c.o.s.limit.AccessLimitInterceptor : 請求過于頻繁請稍后再試

三、API限流如何應用于騰訊地圖開放平臺

API限流在騰訊地圖開放平臺的應用主要體現在以下幾個方面:

  1. 服務限流策略展示: 騰訊云API網關提供了展示服務限流策略的接口,通過這個接口可以查看服務的限流規則,包括默認接口請求頻率限制為20次/秒。這有助于開發者了解和控制對騰訊地圖開放平臺的調用頻率,避免因超出限制而被限制服務。
  2. 多維度精細化限流: 騰訊云API網關支持多維度的限流,包括整個服務、特定用戶等,并且支持不同維度間的自由組合,以滿足多種業務場景的需求。這對于騰訊地圖開放平臺來說,意味著可以針對不同的使用情況和用戶群體設置不同的限流策略,以保護后端服務的穩定性和可用性。
  3. 流量控制插件: 騰訊云API網關提供的流量控制插件可以設置API、應用、ClientIP三種資源維度和秒、分鐘、小時、天四種時間維度的限流。通過創建基礎流控插件并綁定到API,可以有效地保護后端服務不受過多請求的影響,這對于騰訊地圖開放平臺來說,可以減少因流量突增導致的服務不穩定。
  4. API限流規則創建: 騰訊地圖開放平臺還提供了創建API限流規則的接口,允許開發者設置API的QPS(每秒查詢率)限制,以及開啟或禁用限流規則。這為騰訊地圖開放平臺的使用者提供了更靈活的限流控制,可以根據實際業務需求調整限流策略,確保服務的流暢運行。

源碼傳送門:

https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-validated

文章轉自微信公眾號@傳一卓躍

上一篇:

API協議設計的10種技術

下一篇:

借助Serverless框架構建RESTful API
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費