优化Session、SessionManager

master
剑器近 2021-06-24 16:41:22 +08:00
parent 8e570571ad
commit 5734eccd88
9 changed files with 110 additions and 108 deletions

View File

@ -6,6 +6,7 @@ import io.github.yezhihao.netmc.codec.MessageDecoder;
import io.github.yezhihao.netmc.codec.MessageEncoder; import io.github.yezhihao.netmc.codec.MessageEncoder;
import io.github.yezhihao.netmc.core.HandlerInterceptor; import io.github.yezhihao.netmc.core.HandlerInterceptor;
import io.github.yezhihao.netmc.core.HandlerMapping; import io.github.yezhihao.netmc.core.HandlerMapping;
import io.github.yezhihao.netmc.session.SessionListener;
import io.github.yezhihao.netmc.session.SessionManager; import io.github.yezhihao.netmc.session.SessionManager;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
@ -25,6 +26,7 @@ public class NettyConfig {
protected final HandlerMapping handlerMapping; protected final HandlerMapping handlerMapping;
protected final HandlerInterceptor handlerInterceptor; protected final HandlerInterceptor handlerInterceptor;
protected final SessionManager sessionManager; protected final SessionManager sessionManager;
protected final SessionListener sessionListener;
private NettyConfig(int port, private NettyConfig(int port,
int maxFrameLength, int maxFrameLength,
@ -34,7 +36,8 @@ public class NettyConfig {
MessageEncoder encoder, MessageEncoder encoder,
HandlerMapping handlerMapping, HandlerMapping handlerMapping,
HandlerInterceptor handlerInterceptor, HandlerInterceptor handlerInterceptor,
SessionManager sessionManager SessionManager sessionManager,
SessionListener sessionListener
) { ) {
this.port = port; this.port = port;
this.maxFrameLength = maxFrameLength; this.maxFrameLength = maxFrameLength;
@ -45,7 +48,8 @@ public class NettyConfig {
this.handlerMapping = handlerMapping; this.handlerMapping = handlerMapping;
this.handlerInterceptor = handlerInterceptor; this.handlerInterceptor = handlerInterceptor;
this.sessionManager = sessionManager; this.sessionManager = sessionManager;
this.adapter = new TCPServerHandler(this.handlerMapping, this.handlerInterceptor, this.sessionManager); this.sessionListener = sessionListener;
this.adapter = new TCPServerHandler(this.handlerMapping, this.handlerInterceptor, this.sessionManager, this.sessionListener);
} }
public static NettyConfig.Builder custom() { public static NettyConfig.Builder custom() {
@ -63,6 +67,7 @@ public class NettyConfig {
private HandlerMapping handlerMapping; private HandlerMapping handlerMapping;
private HandlerInterceptor handlerInterceptor; private HandlerInterceptor handlerInterceptor;
private SessionManager sessionManager; private SessionManager sessionManager;
private SessionListener sessionListener;
public Builder() { public Builder() {
} }
@ -121,6 +126,11 @@ public class NettyConfig {
return this; return this;
} }
public Builder setSessionListener(SessionListener sessionListener) {
this.sessionListener = sessionListener;
return this;
}
public NettyConfig build() { public NettyConfig build() {
return new NettyConfig( return new NettyConfig(
this.port, this.port,
@ -131,7 +141,8 @@ public class NettyConfig {
this.encoder, this.encoder,
this.handlerMapping, this.handlerMapping,
this.handlerInterceptor, this.handlerInterceptor,
this.sessionManager this.sessionManager,
this.sessionListener
); );
} }
} }

View File

@ -5,6 +5,7 @@ import io.github.yezhihao.netmc.core.HandlerMapping;
import io.github.yezhihao.netmc.core.handler.Handler; import io.github.yezhihao.netmc.core.handler.Handler;
import io.github.yezhihao.netmc.core.model.Message; import io.github.yezhihao.netmc.core.model.Message;
import io.github.yezhihao.netmc.session.Session; import io.github.yezhihao.netmc.session.Session;
import io.github.yezhihao.netmc.session.SessionListener;
import io.github.yezhihao.netmc.session.SessionManager; import io.github.yezhihao.netmc.session.SessionManager;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
@ -30,10 +31,16 @@ public class TCPServerHandler extends ChannelInboundHandlerAdapter {
private SessionManager sessionManager; private SessionManager sessionManager;
public TCPServerHandler(HandlerMapping handlerMapping, HandlerInterceptor interceptor, SessionManager sessionManager) { private SessionListener sessionListener;
public TCPServerHandler(HandlerMapping handlerMapping,
HandlerInterceptor interceptor,
SessionManager sessionManager,
SessionListener sessionListener) {
this.handlerMapping = handlerMapping; this.handlerMapping = handlerMapping;
this.interceptor = interceptor; this.interceptor = interceptor;
this.sessionManager = sessionManager; this.sessionManager = sessionManager;
this.sessionListener = sessionListener;
} }
@Override @Override
@ -75,7 +82,7 @@ public class TCPServerHandler extends ChannelInboundHandlerAdapter {
@Override @Override
public void channelActive(ChannelHandlerContext ctx) { public void channelActive(ChannelHandlerContext ctx) {
Channel channel = ctx.channel(); Channel channel = ctx.channel();
Session session = sessionManager.newSession(channel); Session session = Session.newInstance(channel, sessionManager, sessionListener);
channel.attr(Session.KEY).set(session); channel.attr(Session.KEY).set(session);
log.info("<<<<<终端连接{}", session); log.info("<<<<<终端连接{}", session);
} }

View File

@ -56,7 +56,7 @@ public abstract class AbstractHandlerMapping implements HandlerMapping {
/** /**
* Handler * Handler
*/ */
public Handler getHandler(Object messageType) { public Handler getHandler(int messageId) {
return handlerMap.get(messageType); return handlerMap.get(messageId);
} }
} }

View File

@ -9,6 +9,6 @@ import io.github.yezhihao.netmc.core.handler.Handler;
*/ */
public interface HandlerMapping { public interface HandlerMapping {
Handler getHandler(Object messageType); Handler getHandler(int messageId);
} }

View File

@ -10,10 +10,10 @@ import java.io.Serializable;
public interface Message extends Serializable { public interface Message extends Serializable {
/** 客户端唯一标识 */ /** 客户端唯一标识 */
Serializable getClientId(); String getClientId();
/** 消息类型 */ /** 消息类型 */
Serializable getMessageId(); int getMessageId();
/** 消息类型(日志输出) */ /** 消息类型(日志输出) */
String getMessageName(); String getMessageName();

View File

@ -25,35 +25,37 @@ public class Session {
public static final AttributeKey<Session> KEY = AttributeKey.newInstance(Session.class.getName()); public static final AttributeKey<Session> KEY = AttributeKey.newInstance(Session.class.getName());
protected final Channel channel; protected final Channel channel;
private final SessionManager sessionManager;
private AtomicInteger serialNo = new AtomicInteger(0); private final SessionListener sessionListener;
private boolean registered = false;
private Object clientId;
private final long creationTime; private final long creationTime;
private volatile long lastAccessedTime; private volatile long lastAccessedTime;
private Map<Object, Object> attributes; private final Map<Object, Object> attributes;
private Integer protocolVersion;
private SessionManager sessionManager; private String sessionId;
private String clientId;
private final AtomicInteger serialNo = new AtomicInteger(0);
protected Session(Channel channel, SessionManager sessionManager) {
this(null, channel, sessionManager);
}
protected Session(Class<? extends Enum> sessionKeyClass, Channel channel, SessionManager sessionManager) { private Session(Channel channel, SessionManager sessionManager, SessionListener sessionListener) {
this.channel = channel; this.channel = channel;
this.sessionManager = sessionManager;
this.creationTime = System.currentTimeMillis(); this.creationTime = System.currentTimeMillis();
this.lastAccessedTime = creationTime; this.lastAccessedTime = creationTime;
if (sessionKeyClass != null) this.sessionManager = sessionManager;
this.attributes = new EnumMap(sessionKeyClass); this.sessionListener = sessionListener;
if (sessionManager != null && sessionManager.getSessionKeyClass() != null)
this.attributes = new EnumMap(sessionManager.getSessionKeyClass());
else else
this.attributes = new TreeMap<>(); this.attributes = new TreeMap<>();
} }
public int getId() { public static Session newInstance(Channel channel,
return channel.id().hashCode(); SessionManager sessionManager,
SessionListener sessionListener) {
Session session = new Session(channel, sessionManager, sessionListener);
session.callSessionCreatedListener();
return session;
} }
public int nextSerialNo() { public int nextSerialNo() {
@ -67,19 +69,30 @@ public class Session {
} }
public boolean isRegistered() { public boolean isRegistered() {
return registered; return sessionId != null;
} }
/** /**
* SessionManager * SessionManager
*/ */
public void register(Object clientId) { public void register(Message message) {
this.registered = true; register(message.getClientId(), message);
this.clientId = clientId;
this.sessionManager.put(this.clientId, this);
} }
public Object getClientId() { public void register(String sessionId, Message message) {
if (sessionId == null)
throw new NullPointerException("sessionId not null");
this.sessionId = sessionId;
this.clientId = message.getClientId();
if (sessionManager != null)
sessionManager.add(this);
}
public String getId() {
return sessionId;
}
public String getClientId() {
return clientId; return clientId;
} }
@ -116,49 +129,38 @@ public class Session {
return attributes.remove(name); return attributes.remove(name);
} }
public Integer getProtocolVersion() { public Object getOfflineCache(String clientId) {
return protocolVersion; if (sessionManager != null)
return sessionManager.getOfflineCache(clientId);
return null;
} }
public void setProtocolVersion(int protocolVersion) { public void setOfflineCache(String clientId, Object value) {
this.protocolVersion = protocolVersion; if (sessionManager != null)
sessionManager.setOfflineCache(clientId, value);
} }
public Integer cachedProtocolVersion(Object clientId) { private void callSessionDestroyedListener() {
return this.sessionManager.getVersion(clientId); if (sessionListener != null)
sessionListener.sessionDestroyed(this);
} }
public void recordProtocolVersion(Object clientId, int protocolVersion) { private void callSessionCreatedListener() {
this.protocolVersion = protocolVersion; if (sessionListener != null)
this.sessionManager.putVersion(clientId, protocolVersion); sessionListener.sessionCreated(this);
} }
public void invalidate() { public void invalidate() {
channel.close(); channel.close();
sessionManager.callSessionDestroyedListener(this); callSessionDestroyedListener();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Session that = (Session) o;
return this.getId() == that.getId();
}
@Override
public int hashCode() {
return getId();
} }
@Override @Override
public String toString() { public String toString() {
final StringBuilder sb = new StringBuilder(66); final StringBuilder sb = new StringBuilder(66);
sb.append("[ip=").append(channel.remoteAddress()); sb.append("[ip=").append(channel.remoteAddress());
sb.append(", sid=").append(sessionId);
sb.append(", cid=").append(clientId); sb.append(", cid=").append(clientId);
sb.append(", reg=").append(registered);
sb.append(']'); sb.append(']');
return sb.toString(); return sb.toString();
} }
@ -232,14 +234,20 @@ public class Session {
} }
private static String requestKey(Message request, Class responseClass) { private static String requestKey(Message request, Class responseClass) {
if (Response.class.isAssignableFrom(responseClass)) String className = responseClass.getName();
return Integer.toString(request.getSerialNo()); if (Response.class.isAssignableFrom(responseClass)) {
return responseClass.getName(); int serialNo = request.getSerialNo();
return new StringBuilder(34).append(className).append('.').append(serialNo).toString();
}
return className;
} }
private static String responseKey(Message response) { private static String responseKey(Message response) {
if (response instanceof Response) String className = response.getClass().getName();
return Integer.toString(((Response) response).getResponseSerialNo()); if (response instanceof Response) {
return response.getClass().getName(); int serialNo = ((Response) response).getResponseSerialNo();
return new StringBuilder(34).append(className).append('.').append(serialNo).toString();
}
return className;
} }
} }

View File

@ -2,7 +2,6 @@ package io.github.yezhihao.netmc.session;
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import java.util.Collection; import java.util.Collection;
@ -16,77 +15,54 @@ import java.util.concurrent.TimeUnit;
*/ */
public class SessionManager { public class SessionManager {
private Map<Object, Session> sessionMap; private Map<String, Session> sessionMap;
private Cache<Object, Integer> versionCache; private Cache<String, Object> offlineCache;
private ChannelFutureListener remover; private ChannelFutureListener remover;
private SessionListener sessionListener;
private Class<? extends Enum> sessionKeyClass; private Class<? extends Enum> sessionKeyClass;
public SessionManager() { public SessionManager() {
this(null);
} }
public SessionManager(Class<? extends Enum> sessionKeyClass) { public SessionManager(Class<? extends Enum> sessionKeyClass) {
this(sessionKeyClass, null);
}
public SessionManager(SessionListener sessionListener) {
this(null, sessionListener);
}
public SessionManager(Class<? extends Enum> sessionKeyClass, SessionListener sessionListener) {
this.sessionMap = new ConcurrentHashMap<>(); this.sessionMap = new ConcurrentHashMap<>();
this.versionCache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(); this.offlineCache = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build();
this.sessionKeyClass = sessionKeyClass; this.sessionKeyClass = sessionKeyClass;
this.remover = future -> { this.remover = future -> {
Session session = future.channel().attr(Session.KEY).get(); Session session = future.channel().attr(Session.KEY).get();
if (session != null) { if (session != null) {
sessionMap.remove(session.getClientId(), session); sessionMap.remove(session.getId(), session);
} }
}; };
this.sessionListener = sessionListener;
} }
public Session newSession(Channel channel) { public Session get(String sessionId) {
Session session = new Session(sessionKeyClass, channel, this); return sessionMap.get(sessionId);
callSessionCreatedListener(session);
return session;
}
protected void callSessionDestroyedListener(Session session) {
if (sessionListener != null)
sessionListener.sessionDestroyed(session);
}
protected void callSessionCreatedListener(Session session) {
if (sessionListener != null)
sessionListener.sessionCreated(session);
}
public Session get(Object clientId) {
return sessionMap.get(clientId);
} }
public Collection<Session> all() { public Collection<Session> all() {
return sessionMap.values(); return sessionMap.values();
} }
protected void put(Object clientId, Session newSession) { protected void add(Session newSession) {
Session oldSession = sessionMap.put(clientId, newSession); Session oldSession = sessionMap.put(newSession.getId(), newSession);
if (!newSession.equals(oldSession)) { if (!newSession.equals(oldSession)) {
newSession.channel.closeFuture().addListener(remover); newSession.channel.closeFuture().addListener(remover);
} }
} }
public void putVersion(Object clientId, int version) { public void setOfflineCache(String clientId, Object value) {
versionCache.put(clientId, version); offlineCache.put(clientId, value);
} }
public Integer getVersion(Object clientId) { public Object getOfflineCache(String clientId) {
return versionCache.getIfPresent(clientId); return offlineCache.getIfPresent(clientId);
}
public Class<? extends Enum> getSessionKeyClass() {
return sessionKeyClass;
} }
} }

View File

@ -26,11 +26,11 @@ public class MyHeader {
this.clientId = clientId; this.clientId = clientId;
} }
public Integer getType() { public int getType() {
return type; return type;
} }
public void setType(Integer type) { public void setType(int type) {
this.type = type; this.type = type;
} }

View File

@ -41,7 +41,7 @@ public class MyMessage implements Message {
} }
@Override @Override
public Integer getMessageId() { public int getMessageId() {
return header.getType(); return header.getType();
} }