cometd源码阅读-WebSocketTransport普通消息处理过程(八)

<1>

入口处从开始看

https://www.cnblogs.com/LQBlog/p/16594835.html#autoid-2-3-0

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);
                }
                //处理握手请求
                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 {
                //<2>普通消息
                processMessage(context, message, promise);
            }
        }
    }

<2>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint#processMessage

    private void processMessage(Context context, ServerMessageImpl message, Promise<Boolean> promise) {

        ServerSessionImpl session = context.session;
        //<3>内部调用extends的incoming方法
        _transport.getBayeux().handle(session, message, Promise.from(y ->
                _transport.processReply(session, message.getAssociated(), Promise.from(reply -> {
                    if (reply != null) {
                        context.replies.add(reply);
                    }
                    if (!isMetaConnectDeliveryOnly(session)) {
                        context.sendQueue = true;
                    }
                    // Leave scheduleExpiration unchanged.
                    promise.succeed(true);
                }, promise::fail)), promise::fail));
    }

<3>

org.cometd.server.BayeuxServerImpl#handle

 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();
            //针对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 {
                            //<4>处理消息
                            handle2(session, message, (ServerChannelImpl)createChannelIfAbsent(channelName).getReference(), promise);
                        }
                    }, promise::fail));
                } else {
                    //<4>处理消息
                    handle2(session, message, channel, promise);
                }
            }
        }
    }

<4>

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()) {
            //消息处理
            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);
                    //<5>消息处理
                    publish(session, channel, message, true, Promise.from(published -> promise.succeed(reply), promise::fail));
                }
            }, promise::fail));
        }
    }

<5>

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);
        }
        //channel非/meta和非/service
        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) {
                //<6>处理消息
                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));
    }

<6>

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);
                    //<7>处理消息
                    publish2(session, channel, message, promise);
                } else {
                    ServerMessage.Mutable reply = message.getAssociated();
                    error(reply, "404::message_deleted");
                    promise.succeed(false);
                }
            }, promise::fail));
        } else {
            //<7>处理消息
            publish2(session, channel, message, promise);
        }
    }

<7>

org.cometd.server.BayeuxServerImpl#publish2

private void publish2(ServerSessionImpl session, ServerChannelImpl channel, ServerMessage.Mutable message, Promise<Boolean> promise) {
        //是否是/meta内部协议渠道
        if (channel.isMeta()) {
            notifyMetaHandlers(session, channel, message, promise);
        } else if (channel.isBroadcast()) {//非/meta和/server的渠道
            //<8>
            notifySubscribers(session, channel, message, promise);
        } else {
            promise.succeed(true);
        }
    }

<8>

org.cometd.server.BayeuxServerImpl#notifySubscribers

 private void notifySubscribers(ServerSessionImpl session, ServerChannelImpl channel, Mutable message, Promise<Boolean> promise) {
        Set<String> wildSubscribers = new HashSet<>();
//先通知模糊匹配的 比如/chat/demo 有的客户端订阅了/chat/* AsyncFoldLeft.run(channel.getChannelId().getWilds(),
true, (result, wildName, wildLoop) -> { //获得channel ServerChannelImpl wildChannel = _channels.get(wildName); if (wildChannel == null) { wildLoop.proceed(result); } else { //获得channel的订阅者 Set<ServerSession> subscribers = wildChannel.subscribers(); if (_logger.isDebugEnabled()) { _logger.debug("Notifying {} subscribers on {}", subscribers.size(), wildChannel); } //遍历调用订阅者的获取消息方法 AsyncFoldLeft.run(subscribers, true, (r, subscriber, loop) -> { if (wildSubscribers.add(subscriber.getId())) { //是否允许自己给自己发送消息 当前渠道 if (subscriber == session && !channel.isBroadcastToPublisher()) { loop.proceed(true); } else { //<9>放入消息队列 ((ServerSessionImpl)subscriber).deliver1(session, message, Promise.from(b -> loop.proceed(true), loop::fail)); } } else { loop.proceed(r); } }, Promise.from(y -> wildLoop.proceed(true), wildLoop::fail)); } }, Promise.from(b -> {
//再推送给直接订阅 如:/chat/demo Set
<ServerSession> subscribers = channel.subscribers(); if (_logger.isDebugEnabled()) { _logger.debug("Notifying {} subscribers on {}", subscribers.size(), channel); } AsyncFoldLeft.run(subscribers, true, (result, subscriber, loop) -> { if (!wildSubscribers.contains(subscriber.getId())) { if (subscriber == session && !channel.isBroadcastToPublisher()) { loop.proceed(true); } else { //<9>放入消息队列 ((ServerSessionImpl)subscriber).deliver1(session, message, Promise.from(y -> loop.proceed(true), loop::fail)); } } else { loop.proceed(true); } }, promise); }, promise::fail) ); }

<9>

org.cometd.server.ServerSessionImpl#deliver1

    protected void deliver1(ServerSession sender, ServerMessage.Mutable mutable, Promise<Boolean> promise) {
        if (sender == this && !isBroadcastToPublisher() && ChannelId.isBroadcast(mutable.getChannel())) {
            promise.succeed(false);
        } else {
            //触发session的获取消息前的生命周期
            extendOutgoing(sender, mutable, Promise.from(message -> {
                if (message == null) {
                    promise.succeed(false);
                } else {
                    _bayeux.freeze(message);
                    //获得session listener
                    AsyncFoldLeft.run(_listeners, true, (result, listener, loop) -> {
                        if (listener instanceof MessageListener) {
                            //触发消息监听器
                            notifyOnMessage((MessageListener)listener, sender, message, _bayeux.resolveLoop(loop));
                        } else {
                            loop.proceed(result);
                        }
                    }, Promise.from(b -> {
                        if (b) {
                            //<11>监听器触发成功加入消息到队列
                            deliver2(sender, message, promise);
                        } else {
                            promise.succeed(false);
                        }
                    }, promise::fail));
                }
            }, promise::fail));
        }
    }

<11>

org.cometd.server.ServerSessionImpl#deliver2

 private void deliver2(ServerSession sender, ServerMessage.Mutable message, Promise<Boolean> promise) {
        //<12>将消息加入队列
        Boolean wakeup = enqueueMessage(sender, message);
        if (wakeup == null) {
            promise.succeed(false);
        } else {
            if (wakeup) {
                //消息是否是延迟消息 开启延迟任务推送
                if (message.isLazy()) {
                    flushLazy(message);
                } else {
                    //<13>直接推送
                    flush();
                }
            }
            promise.succeed(true);
        }
    }

<12>

org.cometd.server.ServerSessionImpl#enqueueMessage

  private Boolean enqueueMessage(ServerSession sender, ServerMessage.Mutable message) {
        synchronized (getLock()) {
            for (ServerSessionListener listener : _listeners) {
                if (listener instanceof QueueMaxedListener) {
                    int maxQueueSize = _maxQueue;
                    //当消息达到达到queueSize 通知消息回调 可以持久化到redis或者mysql
                    if (maxQueueSize > 0 && _queue.size() >= maxQueueSize) {
                        if (!notifyQueueMaxed((QueueMaxedListener)listener, this, _queue, sender, message)) {
                            return null;
                        }
                    }
                }
            }
            //将消息加入queue 最终消费者回调消费则成功方法会触发从queue里面获取推送
            addMessage(message);
            //触发已加入消息监听器
            for (ServerSessionListener listener : _listeners) {
                if (listener instanceof QueueListener) {
                    notifyQueued((QueueListener)listener, sender, message);
                }
            }
            return _batch == 0;
        }
    }

<13>

org.cometd.server.ServerSessionImpl#flush

 public void flush() {
        Scheduler scheduler;
        synchronized (getLock()) {
            _lazyTask.cancel();
            scheduler = _scheduler;
        }
        if (_localSession == null) {
            // <14>消息推送的地方org.cometd.server.websocket.common.AbstractWebSocketEndPoint.WebSocketScheduler
            scheduler.schedule();
        } else {
            // Local delivery.
            if (hasNonLazyMessages()) {
                for (ServerMessage msg : takeQueue(Collections.emptyList())) {
                    _localSession.receive(new HashMapMessage(msg), Promise.noop());
                }
            }
        }
    }

<14>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint.WebSocketScheduler#schedule

     @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()) {
                //当前session未超时 表示在线
                if (cancelTimeout(false)) {
                    if (_logger.isDebugEnabled()) {
                        _logger.debug("Resuming suspended {} for {} on {}", message, session, AbstractWebSocketEndPoint.this);
                    }
                    //触发ServerSession.HeartBeatListener
                    session.notifyResumed(message, false);

                    //<15>发送消息  注意这个this
                    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);
                }
            }
        }

<15>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint#resume

 private void resume(Context context, ServerMessage.Mutable message, Promise<Void> promise) {
        ServerMessage.Mutable reply = message.getAssociated();
        ServerSessionImpl session = context.session;
        if (session != null) {
            Map<String, Object> advice = session.takeAdvice(_transport);
            if (advice != null) {
                reply.put(Message.ADVICE_FIELD, advice);
            }
            if (session.isDisconnected()) {
                reply.getAdvice(true).put(Message.RECONNECT_FIELD, Message.RECONNECT_NONE_VALUE);
            }
        }
        _transport.processReply(session, reply, Promise.from(r -> {
            if (r != null) {
                context.replies.add(r);
            }
            context.sendQueue = true;
            context.scheduleExpiration = true;
            //<16>这里的回调是this 最终会调用 org.cometd.server.websocket.common.AbstractWebSocketEndPoint.WebSocketScheduler.succeed方法
            promise.succeed(null);
        }, x -> scheduleExpirationAndFail(session, context.metaConnectCycle, promise, x)));
    }

<16>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint.WebSocketScheduler#succeed

 public void succeed(Void result) {
            //<17>
            executeFlush(context, Promise.from(y -> {}, this::fail));
        }

<17>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint.WebSocketScheduler#executeFlush

     private void executeFlush(Context context, Promise<Void> promise) {
            //<18>线程池异步触发当前ssesion拉取消息
            _transport.getBayeux().execute(() -> AbstractWebSocketEndPoint.this.flush(context, promise));
        }

<18>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint#flush

  protected void flush(Context context, Promise<Void> promise) {
        List<ServerMessage> msgs = Collections.emptyList();
        ServerSessionImpl session = context.session;
        if (context.sendQueue && session != null) {
            //拉取消息
            msgs = session.takeQueue(context.replies);
        }
        if (_logger.isDebugEnabled()) {
            _logger.debug("Flushing {}, replies={}, messages={} on {}", session, context.replies, msgs, this);
        }
        List<ServerMessage> messages = msgs;
        //将消息封装成entry加入推送队列
        boolean queued = flusher.queue(new Entry(context, messages, Promise.from(y -> {
            promise.succeed(null);
            writeComplete(context, messages);
        }, promise::fail)));
        if (queued) {
            //<19>触发消息消费
            flusher.iterate();
        }
    }

<19>

org.cometd.server.websocket.common.AbstractWebSocketEndPoint.Flusher#process

 @Override
        protected Action process() {
            while (true) {
                //类型
                switch (_state) {
                    case IDLE: {
                        synchronized (this) {
                            _entry = _entries.poll();
                        }
                        if (_logger.isDebugEnabled()) {
                            _logger.debug("Processing {} on {}", _entry, AbstractWebSocketEndPoint.this);
                        }
                        if (_entry == null) {
                            return Action.IDLE;
                        }
                        _state = State.HANDSHAKE;
                        _buffer = new StringBuilder(256);
                        break;
                    }
                    case HANDSHAKE: {
                        _state = State.MESSAGES;
                        List<ServerMessage.Mutable> replies = _entry._context.replies;
                        if (!replies.isEmpty()) {
                            ServerMessage.Mutable reply = replies.get(0);
                            if (Channel.META_HANDSHAKE.equals(reply.getChannel())) {
                                if (_logger.isDebugEnabled()) {
                                    _logger.debug("Processing handshake reply {}", reply);
                                }
                                List<ServerMessage> queue = _entry._queue;
                                if (_transport.allowMessageDeliveryDuringHandshake(_session) && !queue.isEmpty()) {
                                    reply.put("x-messages", queue.size());
                                }
                                _transport.getBayeux().freeze(reply);
                                _buffer.setLength(0);
                                _buffer.append("[");
                                _buffer.append(toJSON(reply));
                                _buffer.append("]");
                                ++_replyIndex;
                                AbstractWebSocketEndPoint.this.send(_session, _buffer.toString(), this);
                                return Action.SCHEDULED;
                            }
                        }
                        break;
                    }
                    case MESSAGES: {
                        List<ServerMessage> messages = _entry._queue;
                        int size = messages.size();
                        if (_messageIndex < size) {
                            int batchSize = _transport.getMessagesPerFrame();
                            batchSize = batchSize > 0 ? Math.min(batchSize, size) : size;
                            if (_logger.isDebugEnabled()) {
                                _logger.debug("Processing messages, batch size {}: {}", batchSize, messages);
                            }
                            _buffer.setLength(0);
                            _buffer.append("[");
                            boolean comma = false;
                            int endIndex = Math.min(size, _messageIndex + batchSize);
                            while (_messageIndex < endIndex) {
                                ServerMessage message = messages.get(_messageIndex);
                                if (comma) {
                                    _buffer.append(",");
                                }
                                comma = true;
                                _buffer.append(toJSON(message));
                                ++_messageIndex;
                            }
                            _buffer.append("]");
                            //<20>发送消息
                            AbstractWebSocketEndPoint.this.send(_session, _buffer.toString(), this);
                            return Action.SCHEDULED;
                        }
                        // Start the interval timeout after writing the
                        // messages since they may take time to be written.
                        _entry.scheduleExpiration();
                        _state = State.REPLIES;
                        break;
                    }
                    case REPLIES: {
                        List<ServerMessage.Mutable> replies = _entry._context.replies;
                        int size = replies.size();
                        if (_replyIndex < size) {
                            if (_logger.isDebugEnabled()) {
                                _logger.debug("Processing replies {}", replies);
                            }
                            _buffer.setLength(0);
                            _buffer.append("[");
                            boolean comma = false;
                            while (_replyIndex < size) {
                                ServerMessage.Mutable reply = replies.get(_replyIndex);
                                _transport.getBayeux().freeze(reply);
                                if (comma) {
                                    _buffer.append(",");
                                }
                                comma = true;
                                _buffer.append(toJSON(reply));
                                ++_replyIndex;
                            }
                            _buffer.append("]");
                            AbstractWebSocketEndPoint.this.send(_session, _buffer.toString(), this);
                            return Action.SCHEDULED;
                        }
                        _state = State.COMPLETE;
                        break;
                    }
                    case COMPLETE: {
                        Entry entry = _entry;
                        _state = State.IDLE;
                        // Do not keep the buffer around while we are idle.
                        _buffer = null;
                        _entry = null;
                        _messageIndex = 0;
                        _replyIndex = 0;
                        entry._promise.succeed(null);
                        break;
                    }
                    default: {
                        throw new IllegalStateException("Invalid state " + _state);
                    }
                }
            }
        }

<20>

org.cometd.server.websocket.javax.WebSocketEndPoint.Delegate#send

  @Override
        protected void send(ServerSession session, String data, Callback callback) {
            if (_logger.isDebugEnabled()) {
                _logger.debug("Sending {} on {}", data, this);
            }
            // Async write. 这个session是websocket的session 触发推送
            _wsSession.getAsyncRemote().sendText(data, result -> {
                Throwable failure = result.getException();
                if (failure == null) {
                    callback.succeeded();
                } else {
                    callback.failed(failure);
                }
            });
        }

 

posted @ 2022-09-14 15:37  意犹未尽  阅读(52)  评论(0编辑  收藏  举报