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());
                }
            }
        }
    }

 

posted @ 2022-08-17 13:17  意犹未尽  阅读(110)  评论(0编辑  收藏  举报