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 暂时封禁,体验极差。

✅ 像这样的“小改进”,往往决定了一个项目是从玩具变成可用产品的分水岭。
限流虽小,但它是构建健壮第三方接口调用体系的基础一环。后续还可以结合熔断、重试、缓存等策略进一步增强稳定性。


原始标题:Rate Limiting Access to the Reddit API

« 上一篇: Baeldung周报21