«1. Обзор

В этой быстрой статье мы продолжим улучшать наше небольшое приложение Reddit, ограничивая скорость доступа к API Reddit в реальном времени.

Простая идея заключается в том, что мы хотим убедиться, что мы не слишком сильно ударяем по их API, иначе Reddit начнет блокировать запросы. Мы собираемся хорошо использовать Guava RateLimiter, чтобы добраться туда.

2. Пользовательский шаблон RedditTemplate

Во-первых, давайте создадим шаблон Reddit — небольшой клиент для Reddit API — который объединит всю низкоуровневую коммуникацию в один компонент:

@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();
    }
}

A здесь происходит несколько интересных вещей.

Во-первых, мы используем область сеанса для этого компонента — просто для того, чтобы каждый пользователь/сеанс в нашем приложении получил свой собственный экземпляр RedditTemplate.

Теперь — шаблон OAuth2RestTemplate уже поддерживает сохранение области сеанса учетных данных, но здесь мы выходим за рамки этого и делаем область действия фактического сеанса экземпляра компонента — так что мы также можем ограничивать скорость каждого пользователя отдельно.

Что приводит нас к фактической логике ограничения скорости — проще говоря, мы используем Guava RateLimiter для получения разрешения, прежде чем пропустить запрос и попасть в живой API.

3. RedditController

Далее — давайте начнем использовать этот новый RedditTemplate в RedditContoller — например:

@Controller
public class RedditController {
    @Autowired
    private RedditTemplate redditTemplate;

    @Autowired
    private UserRepository userReopsitory;

    @RequestMapping("/login")
    public String redditLogin() {
        JsonNode node = redditTemplate.getUserInfo();
        
        loadAuthentication(node.get("name").asText(), 
          redditTemplate.getAccessToken());
        return "redirect:home.html";
    }
}

4. Заключение

В этой части примера мы добавили ограничение скорости для приложения Reddit, чтобы убедиться, что живой API не блокирует нас из-за чрезмерной активности.

Это не теоретическая проблема, а то, с чем я сталкивался пару раз, используя приложение.

Именно такого рода небольшие улучшения в конечном итоге приведут к зрелому и пригодному для использования приложению, поэтому я очень рад этому конкретному шагу.