1. 概述
本文将继续优化我们之前开发的小型 Reddit 应用,重点实现 对接口调用频率的限制,避免因请求过于频繁而被 Reddit 封禁。
核心思路很简单:✅ 必须控制对 Reddit API 的调用节奏。否则轻则返回 429,重则 IP 被拉黑。我们选择使用 Google Guava 提供的 RateLimiter
来实现这一目标,简单粗暴且可靠。
2. 自定义 RedditTemplate
首先,我们封装一个 RedditTemplate
—— 一个专门用于与 Reddit API 交互的客户端组件,集中管理所有底层 HTTP 调用。
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RedditTemplate {
@Autowired
@Qualifier("redditRestTemplate")
private OAuth2RestTemplate redditRestTemplate;
private RateLimiter rateLimiter;
public RedditTemplate() {
rateLimiter = RateLimiter.create(1);
}
public JsonNode getUserInfo() {
rateLimiter.acquire();
return redditRestTemplate.getForObject(
"https://oauth.reddit.com/api/v1/me", JsonNode.class);
}
public JsonNode submitPost(MultiValueMap<String, String> params) {
rateLimiter.acquire();
return redditRestTemplate.postForObject(
"https://oauth.reddit.com/api/submit", params, JsonNode.class);
}
public String needsCaptcha() {
rateLimiter.acquire();
return redditRestTemplate.getForObject(
"https://oauth.reddit.com/api/needs_captcha.json", String.class);
}
public String getNewCaptcha() {
rateLimiter.acquire();
Map<String, String> param = new HashMap<String, String>();
param.put("api_type", "json");
return redditRestTemplate.postForObject(
"https://oauth.reddit.com/api/new_captcha", param, String.class, param);
}
public OAuth2AccessToken getAccessToken() {
rateLimiter.acquire();
return redditRestTemplate.getAccessToken();
}
}
有几个关键点需要注意:
✅ 使用
session
作用域:通过@Scope("session")
确保每个用户会话拥有独立的RedditTemplate
实例。- 虽然
OAuth2RestTemplate
本身支持会话级凭证管理,但我们更进一步,将整个 bean 实例隔离到会话级别。 - 这样做的好处是:⚠️ 可以实现 ** per-user 的独立限流**,避免某个活跃用户拖累其他用户。
- 虽然
✅ 统一接入限流控制:所有对外 API 调用前都调用
rateLimiter.acquire()
。RateLimiter.create(1)
表示每秒最多允许 1 次请求(即 1 QPS),符合 Reddit 官方建议的调用频率。- Guava 的
RateLimiter
使用令牌桶算法,平滑限流,不会出现突发流量。
💡 小贴士:如果你的应用有多个用户共享同一个账号调用 Reddit API,这种 per-session 限流依然有效,能防止内部“自爆”。
3. 在 RedditController 中使用
接下来,在控制器中注入并使用这个带限流功能的 RedditTemplate
:
@Controller
public class RedditController {
@Autowired
private RedditTemplate redditTemplate;
@Autowired
private UserRepository userRepository;
@RequestMapping("/login")
public String redditLogin() {
JsonNode node = redditTemplate.getUserInfo();
loadAuthentication(node.get("name").asText(),
redditTemplate.getAccessToken());
return "redirect:home.html";
}
}
- 所有通过
redditTemplate
发起的请求都会自动受到限流保护。 - ❌ 不需要在 Controller 层手动加锁或 sleep,逻辑清晰,职责分离。
4. 总结
本文为 Reddit 应用增加了关键的限流机制,确保在高并发或多用户场景下不会因触发 API 频率限制而导致服务不可用。
这不是纸上谈兵 —— 实际开发中我已经踩过这个坑:测试时模拟多个用户快速操作,结果整个应用被 Reddit 暂时封禁,体验极差。
✅ 像这样的“小改进”,往往决定了一个项目是从玩具变成可用产品的分水岭。
限流虽小,但它是构建健壮第三方接口调用体系的基础一环。后续还可以结合熔断、重试、缓存等策略进一步增强稳定性。