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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章