优化WebSocket
This commit is contained in:
@@ -47,7 +47,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
|
||||
protected Servlet<C, R, ? extends Response<C, R>> servlet;
|
||||
|
||||
protected final ByteBuffer writeBuffer;
|
||||
private final ByteBuffer writeBuffer;
|
||||
|
||||
private final CompletionHandler finishBytesHandler = new CompletionHandler<Integer, Void>() {
|
||||
|
||||
@@ -377,23 +377,29 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
}
|
||||
|
||||
protected <A> void send(final ByteTuple array, final CompletionHandler<Integer, Void> handler) {
|
||||
this.channel.write(array, new CompletionHandler<Integer, Void>() {
|
||||
ByteBuffer buffer = this.writeBuffer;
|
||||
if (buffer != null && buffer.capacity() >= array.length()) {
|
||||
buffer.clear();
|
||||
buffer.put(array.content(), array.offset(), array.length());
|
||||
buffer.flip();
|
||||
this.channel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, Void attachment) {
|
||||
if (handler != null) {
|
||||
handler.completed(result, attachment);
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
attachment.clear();
|
||||
handler.completed(result, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
if (handler != null) {
|
||||
handler.failed(exc, attachment);
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
attachment.clear();
|
||||
handler.failed(exc, null);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.channel.write(array, handler);
|
||||
}
|
||||
}
|
||||
|
||||
protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
|
||||
|
||||
@@ -129,6 +129,30 @@ public class WorkThread extends Thread implements Executor {
|
||||
}
|
||||
}
|
||||
|
||||
public void runWork(int hash, Runnable command) {
|
||||
if (hashExecutor == null) {
|
||||
if (workExecutor == null) {
|
||||
command.run();
|
||||
} else {
|
||||
workExecutor.execute(command);
|
||||
}
|
||||
} else {
|
||||
hashExecutor.execute(hash, command);
|
||||
}
|
||||
}
|
||||
|
||||
public void runWork(java.io.Serializable hash, Runnable command) {
|
||||
if (hashExecutor == null) {
|
||||
if (workExecutor == null) {
|
||||
command.run();
|
||||
} else {
|
||||
workExecutor.execute(command);
|
||||
}
|
||||
} else {
|
||||
hashExecutor.execute(hash, command);
|
||||
}
|
||||
}
|
||||
|
||||
public void runAsync(Runnable command) {
|
||||
if (workExecutor == null) {
|
||||
ForkJoinPool.commonPool().execute(command);
|
||||
|
||||
@@ -9,8 +9,8 @@ import java.nio.channels.CompletionHandler;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.redkale.annotation.ConstructorParameters;
|
||||
import static org.redkale.asm.Opcodes.*;
|
||||
import org.redkale.asm.*;
|
||||
import static org.redkale.asm.Opcodes.*;
|
||||
import org.redkale.net.Context;
|
||||
import org.redkale.util.*;
|
||||
|
||||
|
||||
@@ -1197,7 +1197,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
this.contentLength = buffer == null ? 0 : buffer.remaining();
|
||||
}
|
||||
createHeader();
|
||||
if (buffer == null) {
|
||||
if (buffer == null) { //只发header
|
||||
super.send(headerArray, handler);
|
||||
} else {
|
||||
ByteBuffer headbuf = channel.pollWriteBuffer();
|
||||
|
||||
@@ -827,11 +827,11 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
* 接收到消息前的拦截方法, ping/pong不在其内 <br>
|
||||
* 注意:处理完后需要调用 messageEvent.run() 才能响应onMessage
|
||||
*
|
||||
* @param restmapping Rest的方法名,没有则为空字符串
|
||||
* @param restMapping Rest的方法名,没有则为空字符串
|
||||
* @param param onMessage方法的参数
|
||||
* @param messageEvent onMessage事件
|
||||
*/
|
||||
public void preOnMessage(String restmapping, WebSocketParam param, Runnable messageEvent) {
|
||||
public void preOnMessage(String restMapping, WebSocketParam param, Runnable messageEvent) {
|
||||
messageEvent.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class WebSocketEngine {
|
||||
private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>();
|
||||
|
||||
@Comment("当前连接数")
|
||||
protected final AtomicInteger currconns = new AtomicInteger();
|
||||
protected final AtomicInteger currConns = new AtomicInteger();
|
||||
|
||||
@Comment("用于PING的定时器")
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
@@ -65,7 +65,7 @@ public class WebSocketEngine {
|
||||
protected final Logger logger;
|
||||
|
||||
@Comment("PING的间隔秒数")
|
||||
protected int liveinterval;
|
||||
protected int liveInterval;
|
||||
|
||||
@Comment("最大连接数, 为0表示无限制")
|
||||
protected int wsMaxConns;
|
||||
@@ -82,14 +82,14 @@ public class WebSocketEngine {
|
||||
@Comment("加密解密器")
|
||||
protected Cryptor cryptor;
|
||||
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, int wsMaxConns,
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveInterval, int wsMaxConns,
|
||||
int wsThreads, int wsMaxBody, boolean mergeMode, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
this.engineid = engineid;
|
||||
this.single = single;
|
||||
this.context = context;
|
||||
this.sendConvert = sendConvert;
|
||||
this.node = node;
|
||||
this.liveinterval = liveinterval;
|
||||
this.liveInterval = liveInterval;
|
||||
this.wsMaxConns = wsMaxConns;
|
||||
this.wsThreads = wsThreads;
|
||||
this.wsMaxBody = wsMaxBody;
|
||||
@@ -104,8 +104,8 @@ public class WebSocketEngine {
|
||||
if (conf != null && conf.getAnyValue("properties") != null) {
|
||||
props = conf.getAnyValue("properties");
|
||||
}
|
||||
this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
|
||||
if (liveinterval <= 0) {
|
||||
this.liveInterval = props == null ? (liveInterval < 0 ? DEFAILT_LIVEINTERVAL : liveInterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveInterval < 0 ? DEFAILT_LIVEINTERVAL : liveInterval));
|
||||
if (liveInterval <= 0) {
|
||||
return;
|
||||
}
|
||||
if (props != null) {
|
||||
@@ -125,18 +125,18 @@ public class WebSocketEngine {
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5;
|
||||
final int intervalms = liveinterval * 1000;
|
||||
long delay = (liveInterval - System.currentTimeMillis() / 1000 % liveInterval) + index * 5;
|
||||
final int intervalms = liveInterval * 1000;
|
||||
scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
getLocalWebSockets().stream().filter(x -> ((now - x.getLastReadTime()) > intervalms && (now - x.getLastSendTime()) > intervalms)).forEach(x -> x.sendPing());
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "WebSocketEngine schedule(interval=" + liveinterval + "s) ping error", t);
|
||||
logger.log(Level.SEVERE, "WebSocketEngine schedule(interval=" + liveInterval + "s) ping error", t);
|
||||
}
|
||||
}, delay, liveinterval, TimeUnit.SECONDS);
|
||||
}, delay, liveInterval, TimeUnit.SECONDS);
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(wsmaxconns:" + wsMaxConns + ", delay:" + delay + "s, interval:" + liveinterval + "s) scheduler executor");
|
||||
logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(wsmaxconns:" + wsMaxConns + ", delay:" + delay + "s, interval:" + liveInterval + "s) scheduler executor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public class WebSocketEngine {
|
||||
@Comment("添加WebSocket")
|
||||
CompletableFuture<Void> addLocal(WebSocket socket) {
|
||||
if (single) {
|
||||
currconns.incrementAndGet();
|
||||
currConns.incrementAndGet();
|
||||
websockets.put(socket._userid, socket);
|
||||
} else { //非线程安全, 在常规场景中无需锁
|
||||
List<WebSocket> list = websockets2.get(socket._userid);
|
||||
@@ -157,7 +157,7 @@ public class WebSocketEngine {
|
||||
list = new CopyOnWriteArrayList<>();
|
||||
websockets2.put(socket._userid, list);
|
||||
}
|
||||
currconns.incrementAndGet();
|
||||
currConns.incrementAndGet();
|
||||
list.add(socket);
|
||||
}
|
||||
if (node != null) {
|
||||
@@ -173,7 +173,7 @@ public class WebSocketEngine {
|
||||
return null; //尚未登录成功
|
||||
}
|
||||
if (single) {
|
||||
currconns.decrementAndGet();
|
||||
currConns.decrementAndGet();
|
||||
websockets.remove(userid);
|
||||
if (node != null) {
|
||||
return node.disconnect(userid);
|
||||
@@ -181,7 +181,7 @@ public class WebSocketEngine {
|
||||
} else { //非线程安全, 在常规场景中无需锁
|
||||
List<WebSocket> list = websockets2.get(userid);
|
||||
if (list != null) {
|
||||
currconns.decrementAndGet();
|
||||
currConns.decrementAndGet();
|
||||
list.remove(socket);
|
||||
if (list.isEmpty()) {
|
||||
websockets2.remove(userid);
|
||||
@@ -476,7 +476,7 @@ public class WebSocketEngine {
|
||||
if (this.wsMaxConns < 1) {
|
||||
return false;
|
||||
}
|
||||
return currconns.get() >= this.wsMaxConns;
|
||||
return currConns.get() >= this.wsMaxConns;
|
||||
}
|
||||
|
||||
@Comment("获取所有连接")
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.convert.Convert;
|
||||
import org.redkale.net.AsyncIOThread;
|
||||
import static org.redkale.net.http.WebSocket.*;
|
||||
import org.redkale.net.http.WebSocketPacket.FrameType;
|
||||
import org.redkale.util.ByteArray;
|
||||
@@ -31,8 +32,6 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
|
||||
protected final Logger logger;
|
||||
|
||||
protected final boolean debug;
|
||||
|
||||
protected final List<WebSocketPacket> currPackets = new ArrayList<>();
|
||||
|
||||
protected ByteArray currSeriesMergeMessageBytes;
|
||||
@@ -51,21 +50,23 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
|
||||
protected int halfFrameLength = -1;
|
||||
|
||||
protected AsyncIOThread ioReadThread;
|
||||
|
||||
public WebSocketReadHandler(HttpContext context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer) {
|
||||
this.context = context;
|
||||
this.restMessageConsumer = messageConsumer;
|
||||
this.webSocket = webSocket;
|
||||
this.ioReadThread = webSocket._channel.getReadIOThread();
|
||||
this.logger = context.getLogger();
|
||||
this.debug = context.getLogger().isLoggable(Level.FINEST);
|
||||
}
|
||||
|
||||
public void startRead() {
|
||||
CompletableFuture connectFuture = webSocket.onConnected();
|
||||
if (connectFuture == null) {
|
||||
webSocket._channel.read(this);
|
||||
webSocket._channel.readInIOThread(this);
|
||||
} else {
|
||||
connectFuture.whenComplete((r, t) -> {
|
||||
webSocket._channel.read(this);
|
||||
webSocket._channel.readInIOThread(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -96,6 +97,7 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
*
|
||||
*/
|
||||
protected void readDecode(final ByteBuffer realbuf) {
|
||||
boolean debug = context.getLogger().isLoggable(Level.FINEST);
|
||||
if (debug && realbuf.remaining() > 6) {
|
||||
logger.log(Level.FINEST, "read websocket message's length = " + realbuf.remaining());
|
||||
}
|
||||
@@ -290,6 +292,7 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, ByteBuffer readBuffer) {
|
||||
boolean debug = context.getLogger().isLoggable(Level.FINEST);
|
||||
if (count < 1) {
|
||||
if (debug) {
|
||||
logger.log(Level.FINEST, "WebSocket(" + webSocket + ") abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreateTime()) / 1000 + " seconds");
|
||||
@@ -309,40 +312,48 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
//消息处理
|
||||
for (final WebSocketPacket packet : currPackets) {
|
||||
if (packet.type == FrameType.TEXT) {
|
||||
try {
|
||||
Convert convert = webSocket.getTextConvert();
|
||||
if (restMessageConsumer != null && convert != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageRestType, packet.getPayload()));
|
||||
} else {
|
||||
webSocket.onMessage(packet.getPayload() == null ? null : new String(packet.getPayload(), StandardCharsets.UTF_8), packet.last);
|
||||
ioReadThread.runWork(webSocket._userid, () -> {
|
||||
try {
|
||||
Convert convert = webSocket.getTextConvert();
|
||||
if (restMessageConsumer != null && convert != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageRestType, packet.getPayload()));
|
||||
} else {
|
||||
webSocket.onMessage(packet.getPayload() == null ? null : new String(packet.getPayload(), StandardCharsets.UTF_8), packet.last);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
}
|
||||
});
|
||||
} else if (packet.type == FrameType.BINARY) {
|
||||
try {
|
||||
Convert convert = webSocket.getBinaryConvert();
|
||||
if (restMessageConsumer != null && convert != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageRestType, packet.getPayload()));
|
||||
} else {
|
||||
webSocket.onMessage(packet.getPayload(), packet.last);
|
||||
ioReadThread.runWork(webSocket._userid, () -> {
|
||||
try {
|
||||
Convert convert = webSocket.getBinaryConvert();
|
||||
if (restMessageConsumer != null && convert != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageRestType, packet.getPayload()));
|
||||
} else {
|
||||
webSocket.onMessage(packet.getPayload(), packet.last);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
}
|
||||
});
|
||||
} else if (packet.type == FrameType.PING) {
|
||||
try {
|
||||
webSocket.onPing(packet.getPayload());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
|
||||
}
|
||||
ioReadThread.runWork(webSocket._userid, () -> {
|
||||
try {
|
||||
webSocket.onPing(packet.getPayload());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
|
||||
}
|
||||
});
|
||||
} else if (packet.type == FrameType.PONG) {
|
||||
try {
|
||||
//if (debug) logger.log(Level.FINEST, "WebSocket onMessage by PONG FrameType : " + packet);
|
||||
webSocket.onPong(packet.getPayload());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocket(" + webSocket + ") onPong error (" + packet + ")", e);
|
||||
}
|
||||
ioReadThread.runWork(webSocket._userid, () -> {
|
||||
try {
|
||||
//if (debug) logger.log(Level.FINEST, "WebSocket onMessage by PONG FrameType : " + packet);
|
||||
webSocket.onPong(packet.getPayload());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocket(" + webSocket + ") onPong error (" + packet + ")", e);
|
||||
}
|
||||
});
|
||||
} else if (packet.type == FrameType.CLOSE) {
|
||||
webSocket.initiateClosed = true;
|
||||
if (debug) {
|
||||
@@ -360,7 +371,7 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage error", t);
|
||||
}
|
||||
webSocket._channel.read(this);
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage by received error", e);
|
||||
webSocket.kill(CLOSECODE_WSEXCEPTION, "websocket-received error");
|
||||
}
|
||||
@@ -372,7 +383,7 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
return;
|
||||
}
|
||||
if (exc != null) {
|
||||
if (debug) {
|
||||
if (context.getLogger().isLoggable(Level.FINEST)) {
|
||||
context.getLogger().log(Level.FINEST, "WebSocket(" + webSocket + ") read WebSocketPacket failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreateTime()) / 1000 + " seconds", exc);
|
||||
}
|
||||
webSocket.kill(CLOSECODE_WSEXCEPTION, "read websocket-packet failed");
|
||||
|
||||
@@ -118,7 +118,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
protected Convert sendConvert;
|
||||
|
||||
@Resource(name = "$")
|
||||
protected WebSocketNode node;
|
||||
protected WebSocketNode webSocketNode;
|
||||
|
||||
@Resource(name = RESNAME_SERVER_RESFACTORY)
|
||||
protected ResourceFactory resourceFactory;
|
||||
@@ -127,13 +127,19 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
Type msgtype = String.class;
|
||||
try {
|
||||
for (Method method : this.getClass().getDeclaredMethods()) {
|
||||
if (!method.getName().equals("createWebSocket")) continue;
|
||||
if (method.getParameterCount() > 0) continue;
|
||||
if (!method.getName().equals("createWebSocket")) {
|
||||
continue;
|
||||
}
|
||||
if (method.getParameterCount() > 0) {
|
||||
continue;
|
||||
}
|
||||
Type rt = TypeToken.getGenericType(method.getGenericReturnType(), this.getClass());
|
||||
if (rt instanceof ParameterizedType) {
|
||||
msgtype = ((ParameterizedType) rt).getActualTypeArguments()[1];
|
||||
}
|
||||
if (msgtype == Object.class) msgtype = String.class;
|
||||
if (msgtype == Object.class) {
|
||||
msgtype = String.class;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -144,19 +150,33 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
|
||||
@Override
|
||||
final void preInit(Application application, HttpContext context, AnyValue conf) {
|
||||
if (this.textConvert == null) this.textConvert = jsonConvert;
|
||||
if (this.sendConvert == null) this.sendConvert = jsonConvert;
|
||||
InetSocketAddress addr = context.getServerAddress();
|
||||
if (this.node == null) this.node = createWebSocketNode();
|
||||
if (this.node == null) { //没有部署SNCP,即不是分布式
|
||||
this.node = new WebSocketNodeService();
|
||||
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
|
||||
if (this.textConvert == null) {
|
||||
this.textConvert = jsonConvert;
|
||||
}
|
||||
if (this.sendConvert == null) {
|
||||
this.sendConvert = jsonConvert;
|
||||
}
|
||||
InetSocketAddress addr = context.getServerAddress();
|
||||
if (this.webSocketNode == null) {
|
||||
this.webSocketNode = createWebSocketNode();
|
||||
}
|
||||
if (this.webSocketNode == null) { //没有部署SNCP,即不是分布式
|
||||
this.webSocketNode = new WebSocketNodeService();
|
||||
if (logger.isLoggable(Level.WARNING)) {
|
||||
logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
|
||||
}
|
||||
}
|
||||
if (this.webSocketNode.sendConvert == null) {
|
||||
this.webSocketNode.sendConvert = this.sendConvert;
|
||||
}
|
||||
if (this.messageAgent != null) {
|
||||
this.webSocketNode.messageAgent = this.messageAgent;
|
||||
}
|
||||
if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert;
|
||||
if (this.messageAgent != null) this.node.messageAgent = this.messageAgent;
|
||||
{
|
||||
AnyValue props = conf;
|
||||
if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties");
|
||||
if (conf != null && conf.getAnyValue("properties") != null) {
|
||||
props = conf.getAnyValue("properties");
|
||||
}
|
||||
if (props != null) {
|
||||
String cryptorClass = props.getValue(WEBPARAM__CRYPTOR);
|
||||
if (cryptorClass != null && !cryptorClass.isEmpty()) {
|
||||
@@ -164,28 +184,34 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
Class clazz = Thread.currentThread().getContextClassLoader().loadClass(cryptorClass);
|
||||
this.cryptor = (Cryptor) clazz.getDeclaredConstructor().newInstance();
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, cryptorClass);
|
||||
if (resourceFactory != null && this.cryptor != null) resourceFactory.inject(this.cryptor);
|
||||
if (resourceFactory != null && this.cryptor != null) {
|
||||
resourceFactory.inject(this.cryptor);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new HttpException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (application != null && application.isCompileMode()) return;
|
||||
if (application != null && application.isCompileMode()) {
|
||||
return;
|
||||
}
|
||||
//存在WebSocketServlet,则此WebSocketNode必须是本地模式Service
|
||||
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
|
||||
this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, mergemsg, this.cryptor, this.node, this.sendConvert, logger);
|
||||
this.node.init(conf);
|
||||
this.node.localEngine.init(conf);
|
||||
this.webSocketNode.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
|
||||
this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, mergemsg, this.cryptor, this.webSocketNode, this.sendConvert, logger);
|
||||
this.webSocketNode.init(conf);
|
||||
this.webSocketNode.localEngine.init(conf);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
final void postDestroy(Application application, HttpContext context, AnyValue conf) {
|
||||
this.node.postDestroy(conf);
|
||||
this.webSocketNode.postDestroy(conf);
|
||||
super.destroy(context, conf);
|
||||
if (application != null && application.isCompileMode()) return;
|
||||
this.node.localEngine.destroy(conf);
|
||||
if (application != null && application.isCompileMode()) {
|
||||
return;
|
||||
}
|
||||
this.webSocketNode.localEngine.destroy(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,27 +219,31 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
return this.getClass().getSimpleName().replace("_Dyn", "").toLowerCase().replaceAll("websocket.*$", "").replaceAll("servlet.*$", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override //在IOThread中执行
|
||||
public final void execute(final HttpRequest request, final HttpResponse response) throws IOException {
|
||||
final boolean debug = logger.isLoggable(Level.FINEST);
|
||||
if (!request.isWebSocket()) {
|
||||
if (debug) logger.finest("WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request);
|
||||
if (debug) {
|
||||
logger.log(Level.FINEST, "WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request);
|
||||
}
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
final String key = request.getHeader("Sec-WebSocket-Key");
|
||||
if (key == null) {
|
||||
if (debug) logger.finest("WebSocket connect abort, Not found Sec-WebSocket-Key header. request=" + request);
|
||||
if (debug) {
|
||||
logger.log(Level.FINEST, "WebSocket connect abort, Not found Sec-WebSocket-Key header. request=" + request);
|
||||
}
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
if (this.node.localEngine.isLocalConnLimited()) {
|
||||
if (debug) logger.finest("WebSocket connections limit, wsmaxconns=" + this.node.localEngine.getLocalWsMaxConns());
|
||||
if (this.webSocketNode.localEngine.isLocalConnLimited()) {
|
||||
logger.log(Level.WARNING, "WebSocket connections limit, wsmaxconns=" + this.webSocketNode.localEngine.getLocalWsMaxConns());
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
final WebSocket webSocket = this.createWebSocket();
|
||||
webSocket._engine = this.node.localEngine;
|
||||
webSocket._engine = this.webSocketNode.localEngine;
|
||||
webSocket._channel = response.getChannel();
|
||||
webSocket._messageRestType = this.messageRestType;
|
||||
webSocket._textConvert = textConvert;
|
||||
@@ -221,7 +251,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
webSocket._sendConvert = sendConvert;
|
||||
webSocket._remoteAddress = request.getRemoteAddress();
|
||||
webSocket._remoteAddr = request.getRemoteAddr();
|
||||
webSocket._sncpAddress = this.node.localSncpAddress;
|
||||
webSocket._sncpAddress = this.webSocketNode.localSncpAddress;
|
||||
if (this.permessageDeflate && request.getHeader("Sec-WebSocket-Extensions", "").contains("permessage-deflate")) {
|
||||
webSocket.deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
|
||||
webSocket.inflater = new Inflater(true);
|
||||
@@ -229,13 +259,17 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
initRestWebSocket(webSocket);
|
||||
CompletableFuture<String> sessionFuture = webSocket.onOpen(request);
|
||||
if (sessionFuture == null) {
|
||||
if (debug) logger.finest("WebSocket connect abort, Not found sessionid. request=" + request);
|
||||
if (debug) {
|
||||
logger.log(Level.FINEST, "WebSocket connect abort, Not found sessionid. request=" + request);
|
||||
}
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
BiConsumer<String, Throwable> sessionConsumer = (sessionid, ex) -> {
|
||||
if ((sessionid == null && webSocket.delayPackets == null) || ex != null) {
|
||||
if (debug || ex != null) logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex);
|
||||
if (debug || ex != null) {
|
||||
logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex);
|
||||
}
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
@@ -250,7 +284,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
response.setHeader("Connection", "Upgrade");
|
||||
response.addHeader("Upgrade", "websocket");
|
||||
response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes));
|
||||
if (webSocket.deflater != null) response.addHeader("Sec-WebSocket-Extensions", "permessage-deflate");
|
||||
if (webSocket.deflater != null) {
|
||||
response.addHeader("Sec-WebSocket-Extensions", "permessage-deflate");
|
||||
}
|
||||
|
||||
response.sendBody((ByteBuffer) null, new CompletionHandler<Integer, Void>() {
|
||||
|
||||
@@ -261,30 +297,31 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
Runnable createUseridHandler = () -> {
|
||||
CompletableFuture<Serializable> userFuture = webSocket.createUserid();
|
||||
if (userFuture == null) {
|
||||
if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request);
|
||||
if (debug) {
|
||||
logger.log(Level.FINEST, "WebSocket connect abort, Create userid abort. request = " + request);
|
||||
}
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
userFuture.whenComplete((userid, ex2) -> {
|
||||
if ((userid == null && webSocket.delayPackets == null) || ex2 != null) {
|
||||
if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
|
||||
if (debug || ex2 != null) {
|
||||
logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
|
||||
}
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
Runnable runHandler = () -> {
|
||||
webSocket._userid = userid;
|
||||
if (single && !anyuser) {
|
||||
WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, nex) -> {
|
||||
webSocketNode.existsWebSocket(userid).whenComplete((rs, nex) -> {
|
||||
if (rs) {
|
||||
CompletableFuture<Boolean> rcFuture = webSocket.onSingleRepeatConnect();
|
||||
Consumer<Boolean> task = (oldkilled) -> {
|
||||
if (oldkilled) {
|
||||
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
|
||||
webSocketNode.localEngine.addLocal(webSocket);
|
||||
response.removeChannel();
|
||||
webSocket._readHandler.startRead();
|
||||
// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer);
|
||||
// webSocket._runner = runner;
|
||||
// runner.run(); //context.runAsync(runner);
|
||||
response.finish(true);
|
||||
} else { //关闭新连接
|
||||
response.finish(true);
|
||||
@@ -302,22 +339,16 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
});
|
||||
}
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
|
||||
webSocketNode.localEngine.addLocal(webSocket);
|
||||
response.removeChannel();
|
||||
webSocket._readHandler.startRead();
|
||||
// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer);
|
||||
// webSocket._runner = runner;
|
||||
// runner.run(); //context.runAsync(runner);
|
||||
response.finish(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
|
||||
webSocketNode.localEngine.addLocal(webSocket);
|
||||
response.removeChannel();
|
||||
webSocket._readHandler.startRead();
|
||||
// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer);
|
||||
// webSocket._runner = runner;
|
||||
// runner.run(); //context.runAsync(runner);
|
||||
response.finish(true);
|
||||
}
|
||||
};
|
||||
@@ -327,7 +358,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
CompletableFuture<Integer> cf = webSocket._writeHandler.send(delayPackets.toArray(new WebSocketPacket[delayPackets.size()]));
|
||||
cf.whenComplete((Integer v, Throwable t) -> {
|
||||
if (userid == null || t != null) {
|
||||
if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
|
||||
if (t != null) {
|
||||
logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
|
||||
}
|
||||
response.finish(true);
|
||||
} else {
|
||||
runHandler.run();
|
||||
@@ -344,7 +377,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
CompletableFuture<Integer> cf = webSocket._writeHandler.send(delayPackets.toArray(new WebSocketPacket[delayPackets.size()]));
|
||||
cf.whenComplete((Integer v, Throwable t) -> {
|
||||
if (sessionid == null || t != null) {
|
||||
if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
|
||||
if (t != null) {
|
||||
logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
|
||||
}
|
||||
response.finish(true);
|
||||
} else {
|
||||
createUseridHandler.run();
|
||||
@@ -367,7 +402,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
if (workThread == null || workThread == Thread.currentThread()) {
|
||||
sessionConsumer.accept(sessionid, ex);
|
||||
} else {
|
||||
workThread.execute(() -> sessionConsumer.accept(sessionid, ex));
|
||||
workThread.runWork(() -> sessionConsumer.accept(sessionid, ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@ public class ThreadHashExecutor extends AbstractExecutorService {
|
||||
hashExecutor(hash).execute(command);
|
||||
}
|
||||
|
||||
public void execute(java.io.Serializable hash, Runnable command) {
|
||||
hashExecutor(hash == null ? 0 : hash.hashCode()).execute(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable task) {
|
||||
return hashExecutor(0).submit(task);
|
||||
|
||||
@@ -5,14 +5,11 @@
|
||||
*/
|
||||
package org.redkale.test.websocket;
|
||||
|
||||
import org.redkale.net.http.WebServlet;
|
||||
import org.redkale.net.http.WebSocketServlet;
|
||||
import org.redkale.net.http.WebSocket;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.redkale.annotation.Resource;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.test.rest.*;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -26,7 +23,7 @@ public class ChatWebSocketServlet extends WebSocketServlet {
|
||||
|
||||
@Override
|
||||
public void init(HttpContext context, AnyValue conf) {
|
||||
System.out.println("本实例的WebSocketNode: " + super.node);
|
||||
System.out.println("本实例的WebSocketNode: " + super.webSocketNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user