cometd源码阅读-WebSocketTransport内置协议处理过程(七)
说明
comted内置协议
因为comted 有这些内置协议处理,所以上层协议就是应用层 可以是websoket 也可以是长连接轮训 或者轮训jsonp
/** * 建立握手 * 当建立连接后 需要访问这个渠道进行握手 comted将维护内置的连接信息 * Constant representing the handshake meta channel. */ public final static String META_HANDSHAKE = META + "/handshake"; /** * 建立连接 * 可以理解成是一个心跳,告诉comted自己还活着,以及一定时间断开连接后重新连接续约,同时也会获取到最新消息 * Constant representing the connect meta channel */ public final static String META_CONNECT = META + "/connect"; /** * 订阅渠道 * 订阅渠道,可以通过渠道完成类似聊天室的功能 一对一 一对多 多对多 * Constant representing the subscribe meta channel */ public final static String META_SUBSCRIBE = META + "/subscribe"; /** * 取消订阅渠道 * Constant representing the unsubscribe meta channel */ public final static String META_UNSUBSCRIBE = META + "/unsubscribe"; /** * 断开连接 * Constant representing the disconnect meta channel */ public final static String META_DISCONNECT = META + "/disconnect";
消息接收入口处
org.cometd.server.websocket.javax.WebSocketEndPoint#onMessage
@Override public void onMessage(String data) { if (_logger.isDebugEnabled()) { _logger.debug("WebSocket Text message on {}", this); } try { try { Promise.Completable<Void> completable = new Promise.Completable<>(); _delegate.onMessage(data, completable); // Wait, to apply backpressure to the client. completable.get(); } catch (ExecutionException x) { throw x.getCause(); } } catch (Throwable failure) { if (_logger.isDebugEnabled()) { _logger.debug("", failure); } _delegate.close(1011, failure.toString()); } }
<2>
public void onMessage(String data, Promise<Void> p) { //生成匿名类,设置了成功回调和失败回调 注意 这样的匿名类后面会有很多 Promise<Void> promise = Promise.from(p::succeed, failure -> { //失败回调调用close方法 if (_logger.isDebugEnabled()) { _logger.debug("", failure); } close(1011, failure.toString()); //通知failure失败 p.fail(failure); }); try { //<3>这里调用的 org.cometd.server.AbstractServerTransport.parseMessages 通过_transport处理器解析消息 ServerMessage.Mutable[] messages = _transport.parseMessages(data); if (_logger.isDebugEnabled()) { _logger.debug("Parsed {} messages on {}", messages == null ? -1 : messages.length, this); } //正常消息 对消息进行处理 if (messages != null) {
//<4> processMessages(messages, promise); } else { //消息为空调用succeed 不进行处理 promise.succeed(null); } } catch (ParseException x) { //出现异常消息解析异常调用close close(1011, x.toString()); _logger.warn("Error parsing JSON: {} on {}", data, this, x); //调用succeed通知future完成 promise.succeed(null); } catch (Throwable x) { //调用fail通知 promise.fail(x); } }
<3>
org.cometd.server.AbstractServerTransport#parseMessages(java.lang.String)
/** * 读取message * @param json * @return * @throws ParseException */ public ServerMessage.Mutable[] parseMessages(String json) throws ParseException { //委托个_jsonContext return _jsonContext.parse(json); }
<4>
org.cometd.server.websocket.common.AbstractWebSocketEndPoint#processMessages
private void processMessages(ServerMessage.Mutable[] messages, Promise<Void> promise) { //消息长度为0 调用future的fail if (messages.length == 0) { promise.fail(new IOException("bayeux protocol violation")); } else { ServerSessionImpl session; ServerMessage.Mutable m = messages[0]; //获得session封装到context 如果是握手请求,则重置session重新创建一个新的session 非握手请求根据sessionId或者clientId获取 if (Channel.META_HANDSHAKE.equals(m.getChannel())) { _session = null; //针对握手渠道创建一个内部comted session session = _transport.getBayeux().newServerSession(); session.setAllowMessageDeliveryDuringHandshake(_transport.isAllowMessageDeliveryDuringHandshake()); } else { session = _session; if (session == null) { //如果不是需要握手的链接根据client获取session if (!_transport.isRequireHandshakePerConnection()) { session = _session = (ServerSessionImpl)_transport.getBayeux().getSession(m.getClientId()); } } else if (_transport.getBayeux().getSession(session.getId()) == null) {//根据sessionId未在Bayeu找到则置空session session = _session = null; } } //封装当前session到context Context context = new Context(session); //委托给AsyncFoldLeft.run执行多消息循环处理 result处理状态 message为处理消息 loop为迭代器本身 每次迭代修迭代器状态 AsyncFoldLeft.run(messages, true, (result, message, loop) -> { //<5>处理消息 loop.proceed 表示开启下一次迭代同时更新处理结果 processMessage(messages, context, (ServerMessageImpl)message, Promise.from(b -> loop.proceed(result && b), loop::fail)); }, Promise.from(flush -> { //尝试获取自己的消息 if (flush) { flush(context, promise); } else { promise.succeed(null); } }, promise::fail)); } }
<5>
org.cometd.server.websocket.common.AbstractWebSocketEndPoint#processMessage
private void processMessage(ServerMessage.Mutable[] messages, Context context, ServerMessageImpl message, Promise<Boolean> promise) { if (_logger.isDebugEnabled()) { _logger.debug("Processing {} on {}", message, this); } //为message设置当前transport信息 message.setServerTransport(_transport); //为message_bayeuxContext message.setBayeuxContext(_bayeuxContext); //为session设置transport ServerSessionImpl session = context.session; if (session != null) { session.setServerTransport(_transport); } //获得message channel String channel = message.getChannel(); //是否是握手请求 if (Channel.META_HANDSHAKE.equals(channel)) { if (messages.length > 1) { promise.fail(new IOException("protocol violation")); } else { if (session != null) { //设置为null 握手之前不需要发送消息 session.setScheduler(null); } //<6>处理握手请求 processMetaHandshake(context, message, promise); } } else { //握手之前主动推送消息 if (session != null && session.updateServerEndPoint(this)) { session.setScheduler(new WebSocketScheduler(context, message, 0)); } //是否是续约消息 if (Channel.META_CONNECT.equals(channel)) { processMetaConnect(context, message, Promise.from(proceed -> { if (proceed) { resume(context, message, Promise.from(y -> promise.succeed(true), promise::fail)); } else { promise.succeed(false); } }, promise::fail)); } else { //普通消息 processMessage(context, message, promise); } } }
握手
客服端发送消息会通过cometd经过jettywebsocket扩展的端点收到 并委托给delegate
org.cometd.server.websocket.javax.WebSocketEndPoint#onMessage https://www.cnblogs.com/LQBlog/p/16575739.html#autoid-2-4-0
<6>
org.cometd.server.websocket.common.AbstractWebSocketEndPoint#processMetaHandshake
private void processMetaHandshake(Context context, ServerMessage.Mutable message, Promise<Boolean> promise) { ServerSessionImpl session = context.session; //<7> _transport.getBayeux().handle(session, message, Promise.from(reply -> { _transport.processReply(session, reply, Promise.from(r -> { if (r != null) { context.replies.add(r); if (r.isSuccessful()) { _session = session; } } //握手是否同时 发送消息 context.sendQueue = _transport.allowMessageDeliveryDuringHandshake(session) && r != null && r.isSuccessful(); context.scheduleExpiration = true; promise.succeed(true); }, promise::fail)); }, x -> scheduleExpirationAndFail(session, context.metaConnectCycle, promise, x))); }
<7>
org.cometd.server.BayeuxServerImpl#handle
public void handle(ServerSessionImpl session, ServerMessage.Mutable message, Promise<ServerMessage.Mutable> promise) { ServerMessageImpl reply = (ServerMessageImpl)createReply(message); //消息格式进行校验 可以自己扩展 if (_validation) { String error = validateMessage(message); if (error != null) { error(reply, error); promise.succeed(reply); return; } } extendIncoming(session, message, Promise.from(extPass -> { if (extPass) { if (session != null) { //进入session的Extends生命周期 与前面不同的是这个只针对session session.extendIncoming(message, Promise.from(sessExtPass -> { //处理成功进入handle if (sessExtPass) { //<8>处理消息 handle1(session, message, promise); } else { if (!reply.isHandled()) { error(reply, "404::message_deleted"); } promise.succeed(reply); } }, promise::fail)); } else { //<8>处理消息 handle1(null, message, promise); } } else { if (!reply.isHandled()) { error(reply, "404::message_deleted"); } promise.succeed(reply); } }, promise::fail)); }
<8>
org.cometd.server.BayeuxServerImpl#handle1
private void handle1(ServerSessionImpl session, ServerMessage.Mutable message, Promise<ServerMessage.Mutable> promise) { if (_logger.isDebugEnabled()) { _logger.debug("> {} {}", message, session); } ServerMessage.Mutable reply = message.getAssociated(); //session的有效性进行校验 session断开连接,或者 messageid与clientId对不上同时又不是握手请求 if (session == null || session.isDisconnected() || (!session.getId().equals(message.getClientId()) && !Channel.META_HANDSHAKE.equals(message.getChannel()))) { //消息追加错误 unknownSession(reply); promise.succeed(reply); } else { String channelName = message.getChannel(); //<9>针对meta_connect session 执行续约 session.cancelExpiration(Channel.META_CONNECT.equals(channelName)); if (channelName == null) { error(reply, "400::channel_missing"); promise.succeed(reply); } else { //从服务器获得channel ServerChannelImpl channel = getServerChannel(channelName); if (channel == null) { //channel没有找到则先走创建channel流程 isCreationAuthorized(session, message, channelName, Promise.from(result -> { if (result instanceof Authorizer.Result.Denied) { String denyReason = ((Authorizer.Result.Denied)result).getReason(); error(reply, "403:" + denyReason + ":channel_create_denied"); promise.succeed(reply); } else { //<10>处理消息 handle2(session, message, (ServerChannelImpl)createChannelIfAbsent(channelName).getReference(), promise); } }, promise::fail)); } else { //<10>处理消息 handle2(session, message, channel, promise); } } } }
<9>
org.cometd.server.ServerSessionImpl#cancelExpiration
public void cancelExpiration(boolean metaConnect) { long now = System.nanoTime(); synchronized (getLock()) { //更新最后一次接收客户端消息时间 _messageTime = now; //是否是metaConnection if (metaConnect) { // A /meta/connect was received and possibly // suspended by the server, don't sweep it. //MetaConnection的消息_expireTime改为0 让定时扫描过期session的不再扫描它 _expireTime = 0; } else if (_expireTime != 0) { // A /meta/connect was returned to // the client, and another message was // received, so extend the expiration. //非metaConnection根据设置的过期时间 延长过期时间 long maxInterval = calculateMaxInterval(getServerTransport().getMaxInterval()); _expireTime = Math.max(_expireTime, now + TimeUnit.MILLISECONDS.toNanos(maxInterval)); } } if (_logger.isDebugEnabled()) { _logger.debug("{} expiration for {}", metaConnect ? "Cancelled" : "Delayed", this); } }
<10>
org.cometd.server.BayeuxServerImpl#handle2
private void handle2(ServerSessionImpl session, ServerMessage.Mutable message, ServerChannelImpl channel, Promise<ServerMessage.Mutable> promise) { ServerMessage.Mutable reply = message.getAssociated(); //是否是/meta 内置协议开头的channel if (channel.isMeta()) { //<11>消息处理 publish(session, channel, message, true, Promise.from(published -> promise.succeed(reply), promise::fail)); } else { //校验是否有权限推送 isPublishAuthorized(channel, session, message, Promise.from(result -> { if (result instanceof Authorizer.Result.Denied) { String denyReason = ((Authorizer.Result.Denied)result).getReason(); error(reply, "403:" + denyReason + ":publish_denied"); promise.succeed(reply); } else { reply.setSuccessful(true); //消息处理 publish(session, channel, message, true, Promise.from(published -> promise.succeed(reply), promise::fail)); } }, promise::fail)); } }
<11>
org.cometd.server.BayeuxServerImpl#publish
protected void publish(ServerSessionImpl session, ServerChannelImpl channel, ServerMessage.Mutable message, boolean receiving, Promise<Boolean> promise) { if (_logger.isDebugEnabled()) { _logger.debug("< {} {}", message, session); } if (channel.isBroadcast()) { // Do not leak the clientId to other subscribers // as we are now "sending" this message. message.setClientId(null); // Reset the messageId to avoid clashes with message-based transports such // as websocket whose clients may rely on the messageId to match request/responses. message.setId(null); } //触发Listener通知 notifyListeners(session, channel, message, Promise.from(proceed -> { if (proceed) { //处理消息 publish1(session, channel, message, receiving, promise); } else { ServerMessageImpl reply = (ServerMessageImpl)message.getAssociated(); if (reply != null && !reply.isHandled()) { error(reply, "404::message_deleted"); } promise.succeed(false); } }, promise::fail)); }
<12>
org.cometd.server.BayeuxServerImpl#publish1
private void publish1(ServerSessionImpl session, ServerChannelImpl channel, ServerMessage.Mutable message, boolean receiving, Promise<Boolean> promise) { //非/meta和/service/开头的渠道 if (channel.isBroadcast() || !receiving) { extendOutgoing(session, null, message, Promise.from(result -> { if (result) { // Exactly at this point, we convert the message to JSON and therefore // any further modification will be lost. // This is an optimization so that if the message is sent to a million // subscribers, we generate the JSON only once. // From now on, user code is passed a ServerMessage reference (and not // ServerMessage.Mutable), and we attempt to return immutable data // structures, even if it is not possible to guard against all cases. // For example, it is impossible to prevent things like // ((CustomObject)serverMessage.getData()).change() or // ((Map)serverMessage.getExt().get("map")).put(). //重新格式化消息的json freeze(message); //处理消息 publish2(session, channel, message, promise); } else { ServerMessage.Mutable reply = message.getAssociated(); error(reply, "404::message_deleted"); promise.succeed(false); } }, promise::fail)); } else { //<13>处理消息 publish2(session, channel, message, promise); } }
<13>
org.cometd.server.BayeuxServerImpl#publish2
private void publish2(ServerSessionImpl session, ServerChannelImpl channel, ServerMessage.Mutable message, Promise<Boolean> promise) { //是否是/meta内部协议渠道 if (channel.isMeta()) { //<14> notifyMetaHandlers(session, channel, message, promise); } else if (channel.isBroadcast()) {//非/meta和/server的渠道 notifySubscribers(session, channel, message, promise); } else { promise.succeed(true); } }
<14>
org.cometd.server.BayeuxServerImpl#publish2
private void publish2(ServerSessionImpl session, ServerChannelImpl channel, ServerMessage.Mutable message, Promise<Boolean> promise) { //是否是/meta内部协议渠道 if (channel.isMeta()) { //<15> notifyMetaHandlers(session, channel, message, promise); } else if (channel.isBroadcast()) {//非/meta和/server的渠道 notifySubscribers(session, channel, message, promise); } else { promise.succeed(true); } }
<15>
org.cometd.server.BayeuxServerImpl#notifyMetaHandlers
private void notifyMetaHandlers(ServerSessionImpl session, ServerChannelImpl channel, Mutable message, Promise<Boolean> promise) { switch (channel.getId()) { case Channel.META_HANDSHAKE://握手 //<6> handleMetaHandshake(session, message, promise); break; case Channel.META_CONNECT://续约 //<20> handleMetaConnect(session, message, promise); break; case Channel.META_SUBSCRIBE: //订阅 //<24> handleMetaSubscribe(session, message, promise); break; case Channel.META_UNSUBSCRIBE://取消订阅 //<28> handleMetaUnsubscribe(session, message, promise); break; //<30> case Channel.META_DISCONNECT://断开 handleMetaDisconnect(session, message, promise); break; default: promise.fail(new IllegalStateException("Invalid channel " + channel)); break; } }
<16>
org.cometd.server.BayeuxServerImpl#handleMetaHandshake
private void handleMetaHandshake(ServerSessionImpl session, Mutable message, Promise<Boolean> promise) { BayeuxContext context = message.getBayeuxContext(); if (context != null) { session.setUserAgent(context.getHeader("User-Agent")); } //是否有验证模块 if (_policy != null) { //校验是否能建立握手 _policy.canHandshake(this, session, message, Promise.from(can -> { //允许握手 if (can) { //<17> handleMetaHandshake1(session, message, promise); } else { ServerMessage.Mutable reply = message.getAssociated(); error(reply, "403::handshake_denied"); // The user's SecurityPolicy may have customized the response's advice Map<String, Object> advice = reply.getAdvice(true); if (!advice.containsKey(Message.RECONNECT_FIELD)) { advice.put(Message.RECONNECT_FIELD, Message.RECONNECT_NONE_VALUE); } promise.succeed(false); } }, promise::fail)); } else { //<17> handleMetaHandshake1(session, message, promise); } }
<17>
org.cometd.server.BayeuxServerImpl#handleMetaHandshake1
private void handleMetaHandshake1(ServerSessionImpl session, Mutable message, Promise<Boolean> promise) { ServerMessage.Mutable reply = message.getAssociated(); if (session.handshake(message)) { //<18>加入到内部session addServerSession(session, message); //握手返回 支持的transport 以及版本信息 以及续约时间和超时时间等信息 reply.setSuccessful(true); reply.setClientId(session.getId()); reply.put(Message.VERSION_FIELD, "1.0"); reply.put(Message.MIN_VERSION_FIELD, "1.0"); reply.put(Message.SUPPORTED_CONNECTION_TYPES_FIELD, getAllowedTransports()); //<19> Map<String, Object> adviceOut = session.takeAdvice(message.getServerTransport()); if (adviceOut != null) { reply.put(Message.ADVICE_FIELD, adviceOut); } promise.succeed(true); } else { error(reply, "403::handshake_failed"); promise.succeed(false); } }
<18>
org.cometd.server.BayeuxServerImpl#addServerSession
protected void addServerSession(ServerSessionImpl session, ServerMessage message) { if (_logger.isDebugEnabled()) { _logger.debug("Adding {}", session); } _sessions.put(session.getId(), session); //触发全局的SessionListener for (BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.SessionListener) { notifySessionAdded((SessionListener)listener, session, message); } } //触发session级的AddedListener session.added(message); }
<19>
org.cometd.server.ServerSessionImpl#takeAdvice
public Map<String, Object> takeAdvice(ServerTransport transport) { if (transport == null || transport == _advisedTransport) { // The advice has not changed, so return null. return null; } return createAdvice(transport); } public static final String RECONNECT_FIELD = "reconnect"; public static final String INTERVAL_FIELD = "interval"; public static final String MAX_INTERVAL_FIELD = "maxInterval"; public static final String TIMEOUT_FIELD = "timeout"; private Map<String, Object> createAdvice(ServerTransport transport) { _advisedTransport = transport; // The timeout is calculated based on the values of the session/transport // because we want to send to the client the *next* timeout. long timeout = getTimeout() < 0 ? transport.getTimeout() : getTimeout(); // The interval is calculated using also the transient value // because we want to send to the client the *current* interval. long interval = calculateInterval(transport.getInterval()); Map<String, Object> advice = new HashMap<>(3); advice.put(Message.RECONNECT_FIELD, Message.RECONNECT_RETRY_VALUE); advice.put(Message.INTERVAL_FIELD, interval); advice.put(Message.TIMEOUT_FIELD, timeout); if (transport instanceof AbstractServerTransport) { if (((AbstractServerTransport)transport).isHandshakeReconnect()) { long maxInterval = calculateMaxInterval(transport.getMaxInterval()); advice.put(Message.MAX_INTERVAL_FIELD, maxInterval); } } return advice; }
续约
<20>
org.cometd.server.websocket.common.AbstractWebSocketEndPoint#processMetaConnect
private void processMetaConnect(Context context, ServerMessage.Mutable message, Promise<Boolean> promise) { ServerSessionImpl session = context.session; // Remember the connected status before handling the message. //在处理消息之前,记住连接状态。 boolean wasConnected = session != null && session.isConnected(); //<17> _transport.getBayeux().handle(session, message, Promise.from(reply -> { boolean proceed = true; if (session != null) { boolean maySuspend = !session.shouldSchedule(); boolean metaConnectDelivery = isMetaConnectDeliveryOnly(session); if ((maySuspend || !metaConnectDelivery) && reply.isSuccessful()) { long timeout = session.calculateTimeout(_transport.getTimeout()); if (timeout > 0 && wasConnected && session.isConnected()) { //<22>设置一个超时检查定时任务,并启动 AbstractServerTransport.Scheduler scheduler = suspend(context, message, timeout); //取消旧的任务 设置新的 并续约session session.setScheduler(scheduler); proceed = false; } } if (proceed && session.isDisconnected()) { reply.getAdvice(true).put(Message.RECONNECT_FIELD, Message.RECONNECT_NONE_VALUE); } } promise.succeed(proceed); }, x -> scheduleExpirationAndFail(session, context.metaConnectCycle, promise, x))); }
<21>
org.cometd.server.BayeuxServerImpl#handleMetaConnect
private void handleMetaConnect(ServerSessionImpl session, Mutable message, Promise<Boolean> promise) { ServerMessage.Mutable reply = message.getAssociated(); if (session.connected()) { //设置为true表示 reply.setSuccessful(true); Map<String, Object> adviceIn = message.getAdvice(); if (adviceIn != null) { Number timeout = (Number)adviceIn.get("timeout"); session.updateTransientTimeout(timeout == null ? -1L : timeout.longValue()); Number interval = (Number)adviceIn.get("interval"); session.updateTransientInterval(interval == null ? -1L : interval.longValue()); // Force the server to send the advice, as the client may // have forgotten it (for example because of a reload) session.reAdvise(); } else { session.updateTransientTimeout(-1); session.updateTransientInterval(-1); } Map<String, Object> adviceOut = session.takeAdvice(message.getServerTransport()); if (adviceOut != null) { reply.put(Message.ADVICE_FIELD, adviceOut); } promise.succeed(true); } else { unknownSession(reply); promise.succeed(false); } }
<22>
org.cometd.server.websocket.common.AbstractWebSocketEndPoint#suspend
private AbstractServerTransport.Scheduler suspend(Context context, ServerMessage.Mutable message, long timeout) { if (_logger.isDebugEnabled()) { _logger.debug("Suspended {} on {}", message, this); } //触发sesion的心跳监听器 context.session.notifySuspended(message, timeout); //<23>构造函数启动 return new WebSocketScheduler(context, message, timeout); }
<23>
暂时不晓得具体干嘛 后续再研究
org.cometd.server.websocket.common.AbstractWebSocketEndPoint.WebSocketScheduler
private class WebSocketScheduler implements AbstractServerTransport.Scheduler, Runnable, Promise<Void> { private final Context context; private final ServerMessage.Mutable message; private final AtomicMarkableReference<Scheduler.Task> taskRef; private final AtomicBoolean flushing = new AtomicBoolean(); public WebSocketScheduler(Context context, ServerMessage.Mutable message, long timeout) { this.context = context; this.message = message; this.taskRef = new AtomicMarkableReference<>(timeout > 0 ? _transport.getBayeux().schedule(this, timeout) : null, true); context.metaConnectCycle = _transport.newMetaConnectCycle(); } @Override public long getMetaConnectCycle() { return context.metaConnectCycle; } @Override public void schedule() { ServerSessionImpl session = context.session; boolean metaConnectDelivery = isMetaConnectDeliveryOnly(session); // When delivering only via /meta/connect, we want to behave similarly to HTTP. // Otherwise, the scheduler is not "disabled" by cancelling the // timeout, and it will continue to deliver messages to the client. if (metaConnectDelivery || session.isTerminated()) { if (cancelTimeout(false)) { if (_logger.isDebugEnabled()) { _logger.debug("Resuming suspended {} for {} on {}", message, session, AbstractWebSocketEndPoint.this); } session.notifyResumed(message, false); resume(context, message, this); } } else { // Avoid sending messages if this scheduler has been disabled, so that the // messages remain in the session queue until the next scheduler is set. if (taskRef.isMarked()) { Context ctx = new Context(session); ctx.sendQueue = true; ctx.metaConnectCycle = context.metaConnectCycle; flush(ctx); } } } private void flush(Context context) { // This method may be called concurrently, for example when // two clients publish concurrently on the same channel. // We must avoid to dispatch multiple times, to save threads. if (flushing.compareAndSet(false, true)) { executeFlush(context, Promise.from(y -> { // A thread may have seen flushing=true exactly // when this thread is executing this promise: // re-check whether there are messages to send. flushing.set(false); if (context.session.hasNonLazyMessages()) { flush(context); } }, this::fail)); } } private void executeFlush(Context context, Promise<Void> promise) { _transport.getBayeux().execute(() -> AbstractWebSocketEndPoint.this.flush(context, promise)); } @Override public void cancel() { if (cancelTimeout(true)) { if (_logger.isDebugEnabled()) { _logger.debug("Cancelling suspended {} for {} on {}", message, context.session, AbstractWebSocketEndPoint.this); } _transport.scheduleExpiration(context.session, context.metaConnectCycle); } } @Override public void destroy() { if (cancelTimeout(true)) { close(1000, "Destroy"); } } @Override public void run() { // Executed when the /meta/connect timeout expires. if (cancelTimeout(false)) { if (_logger.isDebugEnabled()) { _logger.debug("Timing out suspended {} for {} on {}", message, context.session, AbstractWebSocketEndPoint.this); } context.session.notifyResumed(message, true); resume(context, message, this); } } private boolean cancelTimeout(boolean disable) { while (true) { Scheduler.Task task = taskRef.getReference(); boolean enabled = taskRef.isMarked(); if (taskRef.compareAndSet(task, null, enabled, !disable)) { if (task == null) { return false; } task.cancel(); return true; } } } @Override public void succeed(Void result) { executeFlush(context, Promise.from(y -> {}, this::fail)); } @Override public void fail(Throwable failure) { close(1011, failure.toString()); } @Override public String toString() { return String.format("%s@%x[cycle=%d,%s@%x]", getClass().getSimpleName(), hashCode(), getMetaConnectCycle(), AbstractWebSocketEndPoint.this.getClass().getSimpleName(), AbstractWebSocketEndPoint.this.hashCode()); } }
订阅
入口处在<15>
协议格式
[ { "id": "2", "channel": "/meta/subscribe", "subscription": "/chat/demo", "clientId": "8wuk2tcgd97c0vs78kqasebf6", "ext": {} }, { "id": "3", "channel": "/meta/subscribe", "subscription": "/members/demo", "clientId": "8wuk2tcgd97c0vs78kqasebf6", "ext": {} } ]
<24>
org.cometd.server.BayeuxServerImpl#handleMetaSubscribe
public static final String SUBSCRIPTION_FIELD = "subscription"; private void handleMetaSubscribe(ServerSessionImpl session, Mutable message, Promise<Boolean> promise) { ServerMessage.Mutable reply = message.getAssociated(); //是否有订阅字段 Object subscriptionField = message.get(Message.SUBSCRIPTION_FIELD); //没有订阅字段构建异常消息 if (subscriptionField == null) { error(reply, "403::subscription_missing"); promise.succeed(false); } else { //兼容性转换为list List<String> subscriptions = toChannelList(subscriptionField); //构建异常消息 if (subscriptions == null) { error(reply, "403::subscription_invalid"); promise.succeed(false); } else { //校验模块 校验是否合法 if (!validateSubscriptions(subscriptions)) { error(reply, "403::subscription_invalid"); promise.succeed(false); } else { //for循环处理 AsyncFoldLeft.run(subscriptions, true, (result, subscription, loop) -> { //从_channels获取 ServerChannelImpl channel = getServerChannel(subscription); //表示还未创建 if (channel == null) { //SecurityPolicy模块 校验是否能够创建渠道 校验通过才进入创建并订阅流程 isCreationAuthorized(session, message, subscription, Promise.from(creationResult -> { //校验失败返回false if (creationResult instanceof Authorizer.Result.Denied) { String denyReason = ((Authorizer.Result.Denied)creationResult).getReason(); error(reply, "403:" + denyReason + ":create_denied"); loop.leave(false); } else { //先创建<25>createChannelIfAbsent订阅流程 再订阅<26> handleMetaSubscribe1(session, message, (ServerChannelImpl)createChannelIfAbsent(subscription).getReference(), resolveLoop(loop)); } }, promise::fail)); } else { //进入订阅流程<26> handleMetaSubscribe1(session, message, channel, resolveLoop(loop)); } }, promise); } } } }
<25>
org.cometd.server.BayeuxServerImpl#createChannelIfAbsent
@Override public MarkedReference<ServerChannel> createChannelIfAbsent(String channelName, Initializer... initializers) { ChannelId channelId; boolean initialized = false; //尝试根据channelName获取 判断是否存在 ServerChannelImpl channel = _channels.get(channelName); if (channel == null) { // Creating the ChannelId will also normalize the channelName. //尝试通过处理过的channelId获取 channelId = new ChannelId(channelName); String id = channelId.getId(); if (!id.equals(channelName)) { channelName = id; channel = _channels.get(channelName); } } else { channelId = channel.getChannelId(); } //表示没有被初始化 if (channel == null) { //新建一个channel ServerChannelImpl candidate = new ServerChannelImpl(this, channelId); //放入_channels channel = _channels.putIfAbsent(channelName, candidate); if (channel == null) { // My candidate channel was added to the map, so I'd better initialize it channel = candidate; if (_logger.isDebugEnabled()) { _logger.debug("Added channel {}", channel); } try { //通知 Initializer实现 可以对ServerChannelImpl做自定义配置 for (Initializer initializer : initializers) { notifyConfigureChannel(initializer, channel); } //调用listeners中ChannelListener的configureChannel方法可以对channel进行自定义配置 for (BayeuxServer.BayeuxServerListener listener : _listeners) { if (listener instanceof ServerChannel.Initializer) { notifyConfigureChannel((Initializer)listener, channel); } } } finally { channel.initialized(); } //调用listeners中ChannelListener的channelAdded表示已经被初始化 for (BayeuxServer.BayeuxServerListener listener : _listeners) { if (listener instanceof BayeuxServer.ChannelListener) { notifyChannelAdded((ChannelListener)listener, channel); } } initialized = true; } } else { channel.resetSweeperPasses(); // Double check if the sweeper removed this channel between the check at the top and here. // This is not 100% fool proof (e.g. this thread is preempted long enough for the sweeper // to remove the channel, but the alternative is to have a global lock) _channels.putIfAbsent(channelName, channel); } // Another thread may add this channel concurrently, so wait until it is initialized channel.waitForInitialized(); return new MarkedReference<>(channel, initialized); }
<26>
org.cometd.server.BayeuxServerImpl#handleMetaSubscribe1
private void handleMetaSubscribe1(ServerSessionImpl session, Mutable message, ServerChannelImpl channel, Promise<Boolean> promise) { ServerMessage.Mutable reply = message.getAssociated(); //校验模块 SecurityPolicy校验是否可订阅 isSubscribeAuthorized(channel, session, message, Promise.from(subscribeResult -> { //不可订阅构建异常信息 if (subscribeResult instanceof Authorizer.Result.Denied) { String denyReason = ((Authorizer.Result.Denied)subscribeResult).getReason(); error(reply, "403:" + denyReason + ":subscribe_denied"); promise.succeed(false); } else { //<27>调用channel的subscribe进行订阅 if (channel.subscribe(session, message)) { reply.setSuccessful(true); promise.succeed(true); } else { error(reply, "403::subscribe_failed"); promise.succeed(false); } } }, promise::fail)); }
<27>
org.cometd.server.ServerChannelImpl#subscribe
protected boolean subscribe(ServerSessionImpl session, ServerMessage message) { if (isService()) { // Subscription to service channels is a no operation. return true; } if (isMeta()) { return false; } resetSweeperPasses(); //在session的subscriptions 加入订阅渠道 if (session.subscribe(this)) { //在渠道加入订阅的session 维护了双向关系可以通过session查询订阅渠道 又可以通过渠道查询所有订阅session if (_subscribers.add(session)) { //触发 channel级listeners通知 for (ServerChannelListener listener : _listeners) { if (listener instanceof SubscriptionListener) { notifySubscribed((SubscriptionListener)listener, session, this, message); } } //触发全局 listeners通知 for (BayeuxServer.BayeuxServerListener listener : _bayeux.getListeners()) { if (listener instanceof BayeuxServer.SubscriptionListener) { notifySubscribed((BayeuxServer.SubscriptionListener)listener, session, this, message); } } } return true; } else { return false; } }
取消订阅
入口处<15>
<28>
org.cometd.server.BayeuxServerImpl#handleMetaUnsubscribe
private void handleMetaUnsubscribe(ServerSessionImpl session, Mutable message, Promise<Boolean> promise) { ServerMessage.Mutable reply = message.getAssociated(); Object subscriptionField = message.get(Message.SUBSCRIPTION_FIELD); if (subscriptionField == null) { error(reply, "403::subscription_missing"); promise.succeed(false); } else { List<String> subscriptions = toChannelList(subscriptionField); if (subscriptions == null) { error(reply, "403::subscription_invalid"); promise.succeed(false); } else { if (!validateSubscriptions(subscriptions)) { error(reply, "403::subscription_invalid"); promise.succeed(false); } else { AsyncFoldLeft.run(subscriptions, true, (result, subscription, loop) -> { ServerChannelImpl channel = getServerChannel(subscription); if (channel == null) { error(reply, "400::channel_missing"); loop.leave(false); } else { //<29>调用channel的unsubscribe方法 if (channel.unsubscribe(session, message)) { reply.setSuccessful(true); loop.proceed(true); } else { error(reply, "403::unsubscribe_failed"); loop.leave(false); } } }, promise); } } } }
<29>
org.cometd.server.ServerChannelImpl#unsubscribe
protected boolean unsubscribe(ServerSessionImpl session, ServerMessage message) { // The unsubscription may arrive when the session // is already disconnected; unsubscribe in any case // Subscriptions to service channels are allowed but // are a no-operation, so be symmetric here if (isService()) { return true; } if (isMeta()) { return false; } //从channel移除 if (_subscribers.remove(session)) { //从session移除 session.unsubscribedFrom(this); //触发channel级的通知 for (ServerChannelListener listener : _listeners) { if (listener instanceof SubscriptionListener) { notifyUnsubscribed((SubscriptionListener)listener, session, this, message); } } //触发session级的通知 for (BayeuxServer.BayeuxServerListener listener : _bayeux.getListeners()) { if (listener instanceof BayeuxServer.SubscriptionListener) { notifyUnsubscribed((BayeuxServer.SubscriptionListener)listener, session, this, message); } } } return true; }
断开连接
入口处<15>
<30>
org.cometd.server.BayeuxServerImpl#handleMetaDisconnect
private void handleMetaDisconnect(ServerSessionImpl session, Mutable message, Promise<Boolean> promise) { ServerMessage.Mutable reply = message.getAssociated(); reply.setSuccessful(true); //<31>将session移除 removeServerSession(session, message, false); //<33>Wake up the possibly pending /meta/connect session.flush(); promise.succeed(true); }
<31>
org.cometd.server.BayeuxServerImpl#removeServerSession
private MarkedReference<ServerSessionImpl> removeServerSession(ServerSession session, ServerMessage message, boolean timeout) { if (_logger.isDebugEnabled()) { _logger.debug("Removing session timeout: {}, {}, message: {}", timeout, session, message); } //移除session ServerSessionImpl removed = _sessions.remove(session.getId()); if (removed != session) { return MarkedReference.empty(); } //触发全局通知 // Invoke BayeuxServer.SessionListener first, so that the application // can be "pre-notified" that a session is being removed before the // application gets notifications of channel unsubscriptions. for (BayeuxServerListener listener : _listeners) { if (listener instanceof SessionListener) { notifySessionRemoved((SessionListener)listener, removed, message, timeout); } } //<32>从订阅的所有channel取消订阅 并触发监听器 boolean connected = removed.removed(message, timeout); return new MarkedReference<>(removed, connected); }
<32>
org.cometd.server.ServerSessionImpl#removed
protected boolean removed(ServerMessage message, boolean timeout) { boolean result; synchronized (getLock()) { result = isHandshook(); _state = timeout ? State.EXPIRED : State.DISCONNECTED; } if (result) { for (ServerChannelImpl channel : subscriptions) { channel.unsubscribe(this); } for (ServerSessionListener listener : _listeners) { if (listener instanceof RemovedListener) { notifyRemoved((RemovedListener)listener, this, message, timeout); } } } return result; }
<33>
org.cometd.server.ServerSessionImpl#flush
public void flush() { Scheduler scheduler; synchronized (getLock()) { _lazyTask.cancel(); scheduler = _scheduler; } //应该是消息推送的逻辑实现 比如websocket if (_localSession == null) { // It's a remote session, schedule delivery and return. //触发发送消息的地方 scheduler.schedule(); } else { // Local delivery. if (hasNonLazyMessages()) { for (ServerMessage msg : takeQueue(Collections.emptyList())) { _localSession.receive(new HashMapMessage(msg), Promise.noop()); } } } }