我正在构建一个与Java Spark中的Spotify API交互的API。我使用了用于令牌管理的授权代码流-这意味着令牌将在一个小时内(对于给定的用户)有效,然后需要刷新。
对于连接其Spotify帐户的每个用户,我创建一个计时器,该计时器将检查该用户在50分钟后是否处于活动状态:
如果是->我刷新用户的令牌。 如果不是->我将删除用户以及用户的令牌,这意味着他们要使用我的服务(出于存储目的)将不得不再次登录。
我还保留了一个带有用户对象的HashMap,其中包含来自每个用户的各种信息,例如其个人资料名称,图像,播放列表等。如果计时器的检查证明该用户处于非活动状态,则也将从HashMap中删除该信息。
问题: 每个Timer对象都会创建一个新线程。从理论上讲,如果有成千上万的用户在使用我的服务,那么就会有成千上万的线程……我的直觉告诉我,这是不可接受的。我似乎无法为此缠住头。我应该如何跟踪每位用户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()方法。
答案 0 :(得分:1)
为我的API的每个用户创建线程都很好吗?
显然没有。
每个线程都有一个线程堆栈,该堆栈至少使用 64K字节,默认情况下为1MB;看到:
因此,如果用户数量增加,则会用完内存。这是不可扩展的。
此外,每次执行刷新时,每个线程都需要唤醒。这需要2个上下文切换和相关的开销。
建议:
UserToken
类,代表每个用户令牌,并包括上次检查令牌的时间戳。PriorityQueue<UserToken>
。TimerTask
从优先级队列中删除需要检查的UserToken
个对象。 UserToken
重新添加到队列中。此方法要求扩展更好。假设N
是经过身份验证的用户数:
N
个线程和TimerTask
个对象。M
分钟唤醒一次,而不是N
个线程每M2
分钟都唤醒一次。O(logN)
。 1-空间由UserToken
对象及其子对象以及优先级队列中的内部“节点”组成。最好使用100到200个字节,尽管这将取决于实现。