用 Spring Boot 实现秒杀系统的流量控制:计数器算法与令牌桶算法
在秒杀系统中,流量控制是至关重要的一环。为了防止瞬时的请求激增导致系统崩溃,我们可以采用计数器算法和令牌桶算法来限制用户的请求频率。本文将结合 Spring Boot,通过具体的代码示例介绍这两种算法,并使用生动的比喻来解释其原理。
1. 计数器算法
计数器算法是一种简单直观的流量控制方法。想象一下,我们有一个包裹计数器,每当用户发起秒杀请求,计数器就加一。当计数器超过设定的阈值时,我们暂时停止接收请求,以避免系统过载。
实现计数器算法的 Spring Boot 代码
@RestController public class SeckillController { private static final int REQUEST_THRESHOLD = 100; // 设定的请求阈值 private static int requestCounter = 0; // 请求计数器 @GetMapping("/seckill") public ResponseEntity<String> seckill() { if (requestCounter < REQUEST_THRESHOLD) { // 处理秒杀逻辑 requestCounter++; return ResponseEntity.ok("秒杀成功!"); } else { // 请求超过阈值,返回限流提示 return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("请求过于频繁,请稍后重试。"); } } }
在这个例子中,SeckillController中的 /seckill 接口使用计数器算法来控制请求频率。当请求计数未超过阈值时,允许秒杀请求;当计数超过阈值时,返回请求过于频繁的提示。
2. 令牌桶算法
令牌桶算法是一种更为灵活的流量控制方式。我们可以将其比喻为一个装有令牌的桶,系统以一定的速率不断往桶中放入令牌。用户请求时,需要从桶中获取令牌,如果桶中没有足够的令牌,则拒绝请求。
实现令牌桶算法的 Spring Boot 代码
import java.util.concurrent.TimeUnit; @RestController public class SeckillController { private static final int REQUEST_RATE = 10; // 令牌桶速率(每秒放入的令牌数) private static final int BUCKET_CAPACITY = 20; // 令牌桶容量 private static int availableTokens = BUCKET_CAPACITY; // 当前可用的令牌数量 private static long lastRefillTime = System.currentTimeMillis(); // 上次令牌补充时间 @GetMapping("/seckill") public ResponseEntity<String> seckill() { refillTokens(); // 补充令牌 if (tryConsumeToken()) { // 处理秒杀逻辑 return ResponseEntity.ok("秒杀成功!"); } else { // 令牌不足,返回限流提示 return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("请求过于频繁,请稍后重试。"); } } private synchronized void refillTokens() { long currentTime = System.currentTimeMillis(); long elapsedTime = currentTime - lastRefillTime; int tokensToAdd = (int) (elapsedTime * (REQUEST_RATE / 1000.0)); availableTokens = Math.min(BUCKET_CAPACITY, availableTokens + tokensToAdd); lastRefillTime = currentTime; } private synchronized boolean tryConsumeToken() { if (availableTokens >= 1) { availableTokens--; return true; } else { return false; } } }
在这个例子中,SeckillController中的 /seckill 接口使用令牌桶算法来控制请求频率。通过 refillTokens 方法定期补充令牌,通过 tryConsumeToken 方法尝试获取令牌,从而控制秒杀请求的处理。