Java tutorial
/* * (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: * qiong.wang () */ package com.wq.wqchat.core.push; import static com.wq.wqchat.common.ServerNodes.GS; import java.util.concurrent.ScheduledExecutorService; import com.wq.wqchat.api.Message; import com.wq.wqchat.api.connection.Connection; import com.wq.wqchat.api.spi.push.IPushMessage; import com.wq.wqchat.common.message.PushMessage; import com.wq.wqchat.common.qps.FlowControl; import com.wq.wqchat.common.router.RemoteRouter; import com.wq.wqchat.core.ack.AckTask; import com.wq.wqchat.core.ack.AckTaskQueue; import com.wq.wqchat.core.router.LocalRouter; import com.wq.wqchat.core.router.RouterCenter; import com.wq.wqchat.tools.common.TimeLine; import com.wq.wqchat.tools.log.Logs; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; 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)); } }