Будет ли создание потока для каждого пользователя моего API хорошо масштабироваться? - PullRequest
1 голос
/ 11 января 2020

Я создаю API, который будет взаимодействовать с Spotify API в Java Spark. Я пошел с потоком кода авторизации для управления токеном - это означает, что токен будет действителен в течение часа (для данного пользователя), а затем необходимо обновить sh.

для каждого подключенного пользователя В своей учетной записи Spotify я создаю таймер, который проверяет, был ли пользователь активен через 50 минут:

Если да -> я повторно ссылаюсь sh на токен пользователя. Если нет -> я удаляю пользователя вместе с токеном пользователя, что означает, что им придется снова войти в систему, если они захотят использовать мой сервис (для целей хранения).

Я также сохраняю HashMap с пользователем -объект, с различной информацией от каждого пользователя, такой как имена его профилей, изображения, списки воспроизведения и т. д. c. Это также удаляется из HashMap, если проверка по таймеру подтверждает, что этот пользователь неактивен.

ПРОБЛЕМА: Каждый объект-таймер создает новый поток. Если бы теоретически были тысячи пользователей, использующих мой сервис, были бы тысячи потоков ... Моя интуиция подсказывает мне, что это недопустимо. Я не могу обернуться вокруг этого. Как мне go собираться отслеживать, когда прошло 50 минут для каждого пользователя, сохраняя при этом как можно меньше потоков и не «перегружая» API? Будем благодарны за любые советы!

Код:

package Authentication;

import Spotify.Users.UserSessions;
import java.util.Date;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

public class RefreshTokens extends TimerTask {
    private UserSessions userSessions;
    private Authentication authentication;
    private String currentUserSession;
    private Timer timer = new Timer(true);

    public RefreshTokens(UserSessions userSessions, Authentication authentication, String currentUserSession) {
        this.userSessions = userSessions;
        this.authentication = authentication;
        this.currentUserSession = currentUserSession;
    }

    public void startAutomaticProcess() {
        timer.schedule(this, 20000, 20000); //runs every 20 seconds for testing purposes
    }

    @Override
    public void run() {
        System.out.println("Automatic process started: " + new Date());
        refresh();
    }

    private void refresh() {
        if (userSessions.contains(currentUserSession)) {
            if (userSessions.get(currentUserSession).isActive()) {
                authentication.refreshToken(userSessions.get(currentUserSession));
            } else {
                System.out.println("User was not active enough and has been removed from the server.");
                System.out.println("----------");
                System.out.println("Size of HashMap before: " + userSessions.getHashMap().size());
                userSessions.getHashMap().remove(currentUserSession);
                System.out.println("Size of HashMap after: " + userSessions.getHashMap().size());
                timer.cancel();
                timer.purge();
            }
        }
    }
}

Я создаю новый экземпляр этого класса для каждого нового пользователя и вызываю метод startAutomaticProcess ().

1 Ответ

1 голос
/ 12 января 2020

Будет ли создание потока для каждого пользователя моего API хорошо масштабироваться?

Очевидно, нет.

Каждый поток имеет стек потоков, который использует не менее 64 Кбайт и по умолчанию 1 МБ; см:

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

Кроме того, каждый поток должен просыпаться каждый раз, когда выполняется refre sh. Это влечет за собой 2 переключения контекста и связанные с этим издержки.

Предложение:

  • Создайте класс UserToken, который представляет каждый пользовательский токен и включает временную метку, когда токен последний раз проверялся.
  • Создание PriorityQueue<UserToken>, упорядоченного по меткам времени токенов.
  • Используйте TimerTask для удаления UserToken объектов из очереди приоритетов, которые необходимо проверить.
  • Когда проверка прошла успешно (т. Е. Пользователь все еще активен), обновите отметку времени и повторно добавьте UserToken в очередь.

Этот подход требует гораздо лучшего масштабирования. Предположим, что N - это число аутентифицированных пользователей:

  • Существует только один поток, а не N потоков и TimerTask объектов.
  • Поток должен активироваться один раз каждые M минут, а не N все потоки просыпаются один раз каждые M2 минут.
  • Требуется менее 500 байт 1 на активного пользователя, а не 64 КБ (минимум ).
  • Приоритетная вставка / повторная вставка очереди стоит дешево и масштабируется как O(logN).

1 - пространство состоит из UserToken объект и его вспомогательные объекты, а также внутренний «узел» в очереди приоритетов. Лучше оценить от 100 до 200 байт, хотя это будет спецификация реализации c.

...