[FEATURE] SessionManager: uuidIndex

This commit is contained in:
Gravit 2020-11-21 20:08:39 +07:00
parent 447866d001
commit 0f8f51ea97
No known key found for this signature in database
GPG key ID: 98A079490768CCE5
6 changed files with 84 additions and 18 deletions

View file

@ -312,6 +312,7 @@ public static class NettyPerformanceConfig {
public boolean usingEpoll; public boolean usingEpoll;
public int bossThread; public int bossThread;
public int workerThread; public int workerThread;
public long sessionLifetimeMs = 24 * 60 * 60 * 1000;
} }
public static class NettyBindAddress { public static class NettyBindAddress {

View file

@ -5,19 +5,20 @@
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.RequiredDAO; import pro.gravit.launchserver.auth.RequiredDAO;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.HookSet;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.stream.Stream;
import java.util.stream.Collectors;
public class SessionManager implements NeedGarbageCollection { public class SessionManager implements NeedGarbageCollection {
public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа
private final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128); private final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128);
private final Map<UUID, Set<Entry>> uuidIndex = new ConcurrentHashMap<>(32);
private final LaunchServer server; private final LaunchServer server;
public HookSet<Client> clientRestoreHook = new HookSet<>();
public SessionManager(LaunchServer server) { public SessionManager(LaunchServer server) {
this.server = server; this.server = server;
@ -25,11 +26,45 @@ public SessionManager(LaunchServer server) {
public boolean addClient(Client client) { public boolean addClient(Client client) {
if(client == null) return false; if(client == null || client.session == null) return false;
clientSet.put(client.session, new Entry(compressClient(client))); remove(client.session);
Entry e = new Entry(compressClient(client), client.session);
clientSet.put(client.session, e);
if(client.isAuth && client.uuid != null) {
Set<Entry> uuidSet = uuidIndex.computeIfAbsent(client.uuid, k -> ConcurrentHashMap.newKeySet());
uuidSet.add(e);
}
return true; return true;
} }
public Stream<UUID> findSessionsByUUID(UUID uuid) {
Set<Entry> set = uuidIndex.get(uuid);
if(set != null) return set.stream().map((e) -> e.sessionUuid);
return null;
}
public boolean removeByUUID(UUID uuid) {
Set<Entry> set = uuidIndex.get(uuid);
if(set != null) {
for(Entry e : set) {
clientSet.remove(e.sessionUuid);
}
set.clear();
uuidIndex.remove(uuid);
}
return false;
}
public Set<UUID> getSavedUUIDs()
{
return uuidIndex.keySet();
}
public void clear() {
clientSet.clear();
uuidIndex.clear();
}
private String compressClient(Client client) { private String compressClient(Client client) {
return Launcher.gsonManager.gson.toJson(client); //Compress using later return Launcher.gsonManager.gson.toJson(client); //Compress using later
} }
@ -46,16 +81,24 @@ private Client restoreFromString(String data) {
} }
} }
if(result.refCount == null) result.refCount = new AtomicInteger(1); if(result.refCount == null) result.refCount = new AtomicInteger(1);
clientRestoreHook.hook(result);
return result; return result;
} }
@Override @Override
public void garbageCollection() { public void garbageCollection() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
clientSet.entrySet().removeIf(entry -> { long session_timeout = server.config.netty.performance.sessionLifetimeMs;
long timestamp = entry.getValue().timestamp; Set<UUID> to_delete = new HashSet<>(32);
return (timestamp + SESSION_TIMEOUT < time); clientSet.forEach((uuid, entry) -> {
long timestamp = entry.timestamp;
if(timestamp + session_timeout < time)
to_delete.add(uuid);
}); });
for(UUID session : to_delete) {
remove(session);
}
to_delete.clear();
} }
@ -71,8 +114,27 @@ public Client getOrNewClient(UUID session) {
return client == null ? new Client(session) : client; return client == null ? new Client(session) : client;
} }
public boolean remove(UUID session) {
Entry e =clientSet.remove(session);
if(e != null) {
Set<Entry> set = uuidIndex.get(session);
if(set != null) {
removeUuidFromIndexSet(set, e, session);
}
return true;
}
return false;
}
private void removeUuidFromIndexSet(Set<Entry> set, Entry e, UUID session) {
set.remove(e);
if(set.isEmpty()) {
uuidIndex.remove(session);
}
}
@Deprecated
public void removeClient(UUID session) { public void removeClient(UUID session) {
clientSet.remove(session); remove(session);
} }
@ -92,10 +154,12 @@ public void loadSessions(Set<Client> set) {
} }
private static class Entry { private static class Entry {
public String data; public String data;
public UUID sessionUuid;
public long timestamp; public long timestamp;
public Entry(String data) { public Entry(String data, UUID sessionUuid) {
this.data = data; this.data = data;
this.sessionUuid = sessionUuid;
this.timestamp = System.currentTimeMillis(); this.timestamp = System.currentTimeMillis();
} }
} }

View file

@ -23,6 +23,7 @@ public class Client {
public boolean checkSign; public boolean checkSign;
public ClientPermissions permissions; public ClientPermissions permissions;
public String username; public String username;
public UUID uuid;
public TrustLevel trustLevel; public TrustLevel trustLevel;
public transient AuthProviderPair auth; public transient AuthProviderPair auth;

View file

@ -104,17 +104,17 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
} }
result.session = clientData.session; result.session = clientData.session;
} }
UUID uuid;
if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) { if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
uuid = pair.handler.auth(aresult); clientData.uuid = pair.handler.auth(aresult);
if (LogHelper.isDebugEnabled()) { if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString()); LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, clientData.uuid.toString());
} }
} else { } else {
uuid = pair.handler.usernameToUUID(aresult.username); clientData.uuid = pair.handler.usernameToUUID(aresult.username);
result.accessToken = null; result.accessToken = null;
} }
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, aresult.username, client, clientData.auth.textureProvider);
result.playerProfile = ProfileByUUIDResponse.getProfile(clientData.uuid, aresult.username, client, clientData.auth.textureProvider);
clientData.type = authType; clientData.type = authType;
sendResult(result); sendResult(result);

View file

@ -23,7 +23,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(Client client) throws IOException { public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(Client client) throws IOException {
CurrentUserRequestEvent.UserInfo result = new CurrentUserRequestEvent.UserInfo(); CurrentUserRequestEvent.UserInfo result = new CurrentUserRequestEvent.UserInfo();
if (client.auth != null && client.isAuth && client.username != null) { if (client.auth != null && client.isAuth && client.username != null) {
UUID uuid = client.auth.handler.usernameToUUID(client.username); UUID uuid = client.uuid != null ? client.uuid : client.auth.handler.usernameToUUID(client.username);
if (uuid != null) { if (uuid != null) {
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider); result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider);
} }

View file

@ -38,7 +38,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Client newClient = new Client(null); Client newClient = new Client(null);
newClient.checkSign = client.checkSign; newClient.checkSign = client.checkSign;
handler.setClient(newClient); handler.setClient(newClient);
if (client.session != null) server.sessionManager.removeClient(client.session); if (client.session != null) server.sessionManager.remove(client.session);
if (exitAll) { if (exitAll) {
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> { service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
Client client1 = webSocketFrameHandler.getClient(); Client client1 = webSocketFrameHandler.getClient();
@ -68,7 +68,7 @@ public static void exit(LaunchServer server, WebSocketFrameHandler wsHandler, Ch
Client newCusClient = new Client(null); Client newCusClient = new Client(null);
newCusClient.checkSign = chClient.checkSign; newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient); wsHandler.setClient(newCusClient);
if (chClient.session != null) server.sessionManager.removeClient(chClient.session); if (chClient.session != null) server.sessionManager.remove(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(reason); ExitRequestEvent event = new ExitRequestEvent(reason);
event.requestUUID = RequestEvent.eventUUID; event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event); wsHandler.service.sendObject(channel, event);