java - 为我的 API 的每个用户创建一个线程是否可以很好地扩展?

标签 java multithreading timer api-design

我正在构建一个 API,它将与 Java Spark 中的 Spotify API 进行交互。我使用授权代码流程进行 token 管理 - 这意味着 token 将在一小时内有效(对于给定用户),然后需要刷新。

对于连接 Spotify 帐户的每个用户,我创建一个计时器,用于在 50 分钟后检查用户是否处于 Activity 状态:

如果是 -> 我刷新用户的 token 。 如果否 -> 我会删除该用户以及用户的 token ,这意味着如果他们想使用我的服务(用于存储目的),则必须再次登录。

我还保留一个带有用户对象的 HashMap,其中包含每个用户的各种信息,例如他们的个人资料名称、图像、播放列表等。如果计时器的检查证明该用户处于非 Activity 状态,这也会从 HashMap 中删除。

问题: 每个计时器对象都会创建一个新线程。理论上,如果有数千个用户使用我的服务,就会有数千个线程......我的直觉告诉我,这是 Not Acceptable 。我似乎无法理解这一点。我应该如何跟踪每个用户 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() 方法。

最佳答案

Will creating a thread for every user of my API scale well?

显然不是。

每个线程都有一个线程堆栈,该堆栈使用至少 64K 字节,默认情况下为 1MB;请参阅:

因此,如果用户数量增加,您将耗尽内存。这是不可扩展的。

此外,每次执行刷新时,每个线程都需要唤醒。这需要 2 次上下文切换和相关的开销。

建议:

  • 创建 UserToken代表每个用户 token 的类,并包含上次检查 token 的时间戳。
  • 创建PriorityQueue<UserToken>这是根据 token 的时间戳排序的。
  • 使用 TimerTask删除 UserToken优先级队列中需要检查的对象。
  • 当检查成功时(即用户仍处于 Activity 状态),更新时间戳并重新添加 UserToken到队列中。

这种方法需要更好的规模。假设N是经过身份验证的用户数量:

  • 只有一个线程,而不是 N线程和 TimerTask对象。
  • 线程需要每隔 M 唤醒一次分钟,而不是 N所有线程每隔 M2 唤醒一次分钟。
  • 每个活跃用户需要少于 500 字节1,而不是 64K(最小值)。
  • 优先级队列插入/重新插入成本低廉,并且可缩放为 O(logN) .
<小时/>

1 - 该空间由 UserToken 组成对象及其附属对象,以及优先级队列中的内部“节点”。 100 到 200 字节是一个更好的估计,尽管这将取决于具体的实现。

关于java - 为我的 API 的每个用户创建一个线程是否可以很好地扩展?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59696682/

相关文章:

java - Spring框架中工厂的正确实现方法

java - vlc4j 无法加载 libvlc.so

java - 显示 ProgressDialog 时暂停 Android 应用程序的主线程

python - pexpect 多线程程序在生成 shell 时挂起

c - 使用条件变量进行异步执行。

c++ - 实现计时器以测量作为参数传递的函数的执行时间

c++ - 一旦另一个事件发生,我的 libevent 计时器就会停止……这正常吗?

java - OnClickListener 无法解析为类型 (Eclipse)

android - 测量屏幕打开了多长时间

c - pthread_join 似乎修改了我的循环索引