com.mpush.core.push.SingleUserPushTask.java Source code

Java tutorial

Introduction

Here is the source code for com.mpush.core.push.SingleUserPushTask.java

Source

/*
 * (C) Copyright 2015-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     ohun@live.cn ()
 */

package com.mpush.core.push;

import com.mpush.api.Message;
import com.mpush.api.connection.Connection;
import com.mpush.api.spi.push.IPushMessage;
import com.mpush.common.ServerNodes;
import com.mpush.common.message.PushMessage;
import com.mpush.common.router.RemoteRouter;
import com.mpush.core.ack.AckTask;
import com.mpush.core.ack.AckTaskQueue;
import com.mpush.common.qps.FlowControl;
import com.mpush.core.router.LocalRouter;
import com.mpush.core.router.RouterCenter;
import com.mpush.tools.common.TimeLine;
import com.mpush.tools.log.Logs;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;

import java.util.concurrent.ScheduledExecutorService;

import static com.mpush.common.ServerNodes.GS;

/**
 * Created by ohun on 16/10/24.
 *
 * @author ohun@live.cn ()
 */
public final class SingleUserPushTask implements PushTask, ChannelFutureListener {

    private final FlowControl flowControl;

    private final IPushMessage message;

    private int messageId;

    private long start;

    private final TimeLine timeLine = new TimeLine();

    public SingleUserPushTask(IPushMessage message, FlowControl flowControl) {
        this.flowControl = flowControl;
        this.message = message;
        this.timeLine.begin("push-center-begin");
    }

    @Override
    public ScheduledExecutorService getExecutor() {
        return ((Message) message).getConnection().getChannel().eventLoop();
    }

    /**
     * ?PushClient???Push?
     * <p>
     * ???IP
     * <p>
     * ??
     * <p>
     * 1.?
     * 2.
     * 3.PushClient???
     * <p>
     * ???, ??
     * 1.? (2)
     * 2.??PushClient???? (1,3)
     * <p>
     */
    @Override
    public void run() {
        if (checkTimeout())
            return;// 

        if (checkLocal(message))
            return;// 

        checkRemote(message);//?
    }

    private boolean checkTimeout() {
        if (start > 0) {
            if (System.currentTimeMillis() - start > message.getTimeoutMills()) {

                PushCenter.I.getPushListener().onTimeout(message, timeLine.timeoutEnd().getTimePoints());

                Logs.PUSH.info("[SingleUserPush] push message to client timeout, timeLine={}, message={}", timeLine,
                        message);
                return true;
            }
        } else {
            start = System.currentTimeMillis();
        }
        return false;
    }

    /**
     * ??
     * ??
     *
     * @param message message
     * @return true/false true:success
     */
    private boolean checkLocal(IPushMessage message) {
        String userId = message.getUserId();
        int clientType = message.getClientType();
        LocalRouter localRouter = RouterCenter.I.getLocalRouterManager().lookup(userId, clientType);

        //1.???
        if (localRouter == null)
            return false;

        Connection connection = localRouter.getRouteValue();

        //2.??
        if (!connection.isConnected()) {

            Logs.PUSH.warn("[SingleUserPush] find local router but conn disconnected, message={}, conn={}", message,
                    connection);

            //?
            RouterCenter.I.getLocalRouterManager().unRegister(userId, clientType);

            return false;
        }

        //3.TCP?
        if (!connection.getChannel().isWritable()) {
            PushCenter.I.getPushListener().onFailure(message, timeLine.failureEnd().getTimePoints());

            Logs.PUSH.error(
                    "[SingleUserPush] push message to client failure, tcp sender too busy, message={}, conn={}",
                    message, connection);
            return true;
        }

        //4. qps, ??????
        if (flowControl.checkQps()) {
            timeLine.addTimePoint("before-send");
            //5.???
            PushMessage pushMessage = PushMessage.build(connection).setContent(message.getContent());
            pushMessage.getPacket().addFlag(message.getFlags());
            messageId = pushMessage.getSessionId();
            pushMessage.send(this);
        } else {//??, ???
            PushCenter.I.delayTask(flowControl.getDelay(), this);
        }
        return true;
    }

    /**
     * 
     * ??
     * ?
     * PushClient?
     *
     * @param message message
     */
    private void checkRemote(IPushMessage message) {
        String userId = message.getUserId();
        int clientType = message.getClientType();
        RemoteRouter remoteRouter = RouterCenter.I.getRemoteRouterManager().lookup(userId, clientType);

        // 1.??, ?
        if (remoteRouter == null || remoteRouter.isOffline()) {

            PushCenter.I.getPushListener().onOffline(message, timeLine.end("offline-end").getTimePoints());

            Logs.PUSH.info("[SingleUserPush] remote router not exists user offline, message={}", message);

            return;
        }

        //2.???
        if (remoteRouter.getRouteValue().isThisPC(GS.getHost(), GS.getPort())) {

            PushCenter.I.getPushListener().onOffline(message, timeLine.end("offline-end").getTimePoints());

            //
            RouterCenter.I.getRemoteRouterManager().unRegister(userId, clientType);

            Logs.PUSH.info(
                    "[SingleUserPush] find remote router in this pc, but local router not exists, userId={}, clientType={}, router={}",
                    userId, clientType, remoteRouter);

            return;
        }

        //3.??????PushClient?
        PushCenter.I.getPushListener().onRedirect(message, timeLine.end("redirect-end").getTimePoints());

        Logs.PUSH.info("[SingleUserPush] find router in another pc, userId={}, clientType={}, router={}", userId,
                clientType, remoteRouter);

    }

    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (checkTimeout())
            return;

        if (future.isSuccess()) {//??

            if (message.isNeedAck()) {//?ACK, ?ACK
                addAckTask(messageId);
            } else {
                PushCenter.I.getPushListener().onSuccess(message, timeLine.successEnd().getTimePoints());
            }

            Logs.PUSH.info("[SingleUserPush] push message to client success, timeLine={}, message={}", timeLine,
                    message);

        } else {//?

            PushCenter.I.getPushListener().onFailure(message, timeLine.failureEnd().getTimePoints());

            Logs.PUSH.error("[SingleUserPush] push message to client failure, message={}, conn={}", message,
                    future.channel());
        }
    }

    /**
     * ACK, ?
     *
     * @param messageId ?ack?sessionId
     */
    private void addAckTask(int messageId) {
        timeLine.addTimePoint("waiting-ack");

        //??????
        message.finalized();

        AckTask task = AckTask.from(messageId).setCallback(new PushAckCallback(message, timeLine));

        AckTaskQueue.I.add(task, message.getTimeoutMills() - (int) (System.currentTimeMillis() - start));
    }
}