Java tutorial
/* * (C) 2007-2012 Alibaba Group Holding Limited. * * 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. */ package com.alibaba.napoli.gecko.service.impl; import java.net.InetSocketAddress; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.alibaba.napoli.gecko.core.command.Constants; import com.alibaba.napoli.gecko.core.command.RequestCommand; import com.alibaba.napoli.gecko.core.command.ResponseCommand; import com.alibaba.napoli.gecko.core.command.ResponseStatus; import com.alibaba.napoli.gecko.core.command.kernel.HeartBeatRequestCommand; import com.alibaba.napoli.gecko.core.core.Handler; import com.alibaba.napoli.gecko.core.core.Session; import com.alibaba.napoli.gecko.core.nio.NioSession; import com.alibaba.napoli.gecko.core.nio.impl.TimerRef; import com.alibaba.napoli.gecko.core.util.ExceptionMonitor; import com.alibaba.napoli.gecko.core.util.RemotingUtils; import com.alibaba.napoli.gecko.service.Connection; import com.alibaba.napoli.gecko.service.ConnectionLifeCycleListener; import com.alibaba.napoli.gecko.service.RemotingController; import com.alibaba.napoli.gecko.service.RemotingServer; import com.alibaba.napoli.gecko.service.RequestProcessor; import com.alibaba.napoli.gecko.service.SingleRequestCallBackListener; import com.alibaba.napoli.gecko.service.exception.IllegalMessageException; import com.alibaba.napoli.gecko.service.exception.NotifyRemotingException; /** * * ? * * @author boyan * * @since 1.0, 2009-12-15 ?11:14:51 */ public class GeckoHandler implements Handler { /** * ? * * @author boyan * */ private static final class ProcessorRunner<T extends RequestCommand> implements Runnable { private final DefaultConnection defaultConnection; private final RequestProcessor<T> processor; private final T message; private ProcessorRunner(final DefaultConnection defaultConnection, final RequestProcessor<T> processor, final T message) { this.defaultConnection = defaultConnection; this.processor = processor; this.message = message; } public void run() { this.processor.handleRequest(this.message, this.defaultConnection); } } /** * ? * * @author boyan * */ private final static class HeartBeatListener implements SingleRequestCallBackListener { private final Connection conn; static final String HEARBEAT_FAIL_COUNT = "connection_heartbeat_fail_count"; public ThreadPoolExecutor getExecutor() { return null; } private HeartBeatListener(final Connection conn) { this.conn = conn; } public void onException(final Exception e) { this.innerCloseConnection(this.conn); } public void onResponse(final ResponseCommand responseCommand, final Connection conn) { if (responseCommand == null || responseCommand.getResponseStatus() != ResponseStatus.NO_ERROR) { Integer count = (Integer) this.conn.setAttributeIfAbsent(HEARBEAT_FAIL_COUNT, 1); if (count != null) { count++; if (count < 3) { conn.setAttribute(HEARBEAT_FAIL_COUNT, count); } else { this.innerCloseConnection(conn); } } } else { this.conn.removeAttribute(HEARBEAT_FAIL_COUNT); } } private void innerCloseConnection(final Connection conn) { log.info("" + conn.getRemoteSocketAddress() + ",?" + conn.getGroupSet()); try { conn.close(true); } catch (final NotifyRemotingException e) { log.error("", e); } } } private final DefaultRemotingContext remotingContext; private final RemotingController remotingController; private ReconnectManager reconnectManager; private static final Log log = LogFactory.getLog(GeckoHandler.class); public void setReconnectManager(final ReconnectManager reconnectManager) { this.reconnectManager = reconnectManager; } private void responseThreadPoolBusy(final Session session, final Object msg, final DefaultConnection defaultConnection) { if (defaultConnection != null && msg instanceof RequestCommand) { try { defaultConnection.response(defaultConnection.getRemotingContext().getCommandFactory() .createBooleanAckCommand(((RequestCommand) msg).getRequestHeader(), ResponseStatus.THREADPOOL_BUSY, "?")); } catch (final NotifyRemotingException e) { this.onExceptionCaught(session, e); } } } public GeckoHandler(final RemotingController remotingController) { this.remotingContext = (DefaultRemotingContext) remotingController.getRemotingContext(); this.remotingController = remotingController; } public void onExceptionCaught(final Session session, final Throwable throwable) { if (throwable.getCause() != null) { ExceptionMonitor.getInstance().exceptionCaught(throwable.getCause()); } else { ExceptionMonitor.getInstance().exceptionCaught(throwable); } } public void onMessageReceived(final Session session, final Object message) { final DefaultConnection defaultConnection = this.remotingContext .getConnectionBySession((NioSession) session); if (defaultConnection == null) { log.error("Connection[" + RemotingUtils.getAddrString(session.getRemoteSocketAddress()) + "]???"); session.close(); return; } if (message instanceof RequestCommand) { this.processRequest(session, message, defaultConnection); } else if (message instanceof ResponseCommand) { this.processResponse(message, defaultConnection); } else { throw new IllegalMessageException("?" + message); } } protected void processResponse(final Object message, final DefaultConnection defaultConnection) { final ResponseCommand responseCommand = (ResponseCommand) message; responseCommand.setResponseHost(defaultConnection.getRemoteSocketAddress()); responseCommand.setResponseTime(System.currentTimeMillis()); final RequestCallBack requestCallBack = defaultConnection.getRequestCallBack(responseCommand.getOpaque()); if (requestCallBack != null) { requestCallBack.onResponse(null, responseCommand, defaultConnection); } } @SuppressWarnings("unchecked") protected <T extends RequestCommand> void processRequest(final Session session, final Object message, final DefaultConnection defaultConnection) { final RequestProcessor<T> processor = this.getProcessorByMessage(message); if (processor == null) { log.error("" + message.getClass().getCanonicalName() + "?"); this.responseNoProcessor(session, message, defaultConnection); return; } else { this.executeProcessor(session, (T) message, defaultConnection, processor); } } @SuppressWarnings("unchecked") private <T extends RequestCommand> RequestProcessor<T> getProcessorByMessage(final Object message) { final RequestProcessor<T> processor; if (message instanceof HeartBeatRequestCommand) { processor = (RequestProcessor<T>) this.remotingContext.processorMap.get(HeartBeatRequestCommand.class); } else { processor = (RequestProcessor<T>) this.remotingContext.processorMap.get(message.getClass()); } return processor; } /** * Processor * * @param session * @param message * @param defaultConnection * @param processor */ private <T extends RequestCommand> void executeProcessor(final Session session, final T message, final DefaultConnection defaultConnection, final RequestProcessor<T> processor) { if (processor.getExecutor() == null) { processor.handleRequest(message, defaultConnection); } else { try { processor.getExecutor().execute(new ProcessorRunner<T>(defaultConnection, processor, message)); } catch (final RejectedExecutionException e) { this.responseThreadPoolBusy(session, message, defaultConnection); } } } private void responseNoProcessor(final Session session, final Object message, final DefaultConnection defaultConnection) { if (defaultConnection != null && message instanceof RequestCommand) { try { defaultConnection.response(defaultConnection.getRemotingContext().getCommandFactory() .createBooleanAckCommand(((RequestCommand) message).getRequestHeader(), ResponseStatus.NO_PROCESSOR, "??" + message.getClass().getCanonicalName())); } catch (final NotifyRemotingException e) { this.onExceptionCaught(session, e); } } } public void onMessageSent(final Session session, final Object msg) { } public void onSessionClosed(final Session session) { final InetSocketAddress remoteSocketAddress = session.getRemoteSocketAddress(); final DefaultConnection conn = this.remotingContext.getConnectionBySession((NioSession) session); if (conn == null) { session.close(); return; } log.debug("" + RemotingUtils.getAddrString(remoteSocketAddress) + ",?" + conn.getGroupSet()); // ??? if (conn.isAllowReconnect() && this.reconnectManager != null) { this.waitForReady(conn); this.addReconnectTask(remoteSocketAddress, conn); } // this.removeFromGroups(conn); // ?callBack conn.dispose(); // sessionconnection this.remotingContext.removeSession2ConnectionMapping((NioSession) session); this.adjustMaxScheduleWrittenBytes(); this.remotingContext.notifyConnectionClosed(conn); } private void removeFromGroups(final DefaultConnection conn) { // for (final String group : conn.getGroupSet()) { this.remotingContext.removeConnectionFromGroup(group, conn); } } private void addReconnectTask(final InetSocketAddress remoteSocketAddress, final DefaultConnection conn) { // make a copy final Set<String> groupSet = conn.getGroupSet(); log.info("" + RemotingUtils.getAddrString(remoteSocketAddress) + "??"); // ? synchronized (conn) { if (!groupSet.isEmpty() && !this.hasOnlyDefaultGroup(groupSet) && conn.isAllowReconnect()) { this.reconnectManager.addReconnectTask(new ReconnectTask(groupSet, remoteSocketAddress)); // ?????? conn.setAllowReconnect(false); } } } private boolean hasOnlyDefaultGroup(final Set<String> groupSet) { return groupSet.size() == 1 && groupSet.contains(Constants.DEFAULT_GROUP); } private void waitForReady(final DefaultConnection conn) { /** * ??????? */ synchronized (conn) { int count = 0; while (!conn.isReady() && conn.isAllowReconnect() && count++ < 3) { try { conn.wait(5000); } catch (final InterruptedException e) { // ??? Thread.currentThread().interrupt(); } } } } @SuppressWarnings("unchecked") public void onSessionConnected(final Session session, final Object... args) { final Set<String> groupSet = (Set<String>) args[0]; if (args.length >= 3) { final TimerRef timerRef = (TimerRef) args[2]; if (timerRef != null) { timerRef.cancel(); } } final DefaultConnection conn = this.remotingContext.getConnectionBySession((NioSession) session); try { // ?groupSetsession? if (conn == null || groupSet.isEmpty()) { // ? session.close(); log.error("connection"); } else { this.addConnection2Group(conn, groupSet); } } finally { // ? if (conn != null && conn.isConnected()) { this.notifyConnectionReady(conn); } } } private void addConnection2Group(final DefaultConnection conn, final Set<String> groupSet) { if (groupSet.isEmpty() || this.hasOnlyDefaultGroup(groupSet)) { this.closeConnectionWithoutReconnect(conn); return; } // for (final String group : groupSet) { final Object attribute = this.remotingController.getAttribute(group, Constants.CONNECTION_COUNT_ATTR); if (attribute == null) { // ?? log.info("" + group + "?"); this.closeConnectionWithoutReconnect(conn); return; } else { final int maxConnCount = (Integer) attribute; /** * ???? */ synchronized (this) { // if (this.remotingController.getConnectionCount(group) < maxConnCount) { this.addConnectionToGroup(conn, group, maxConnCount); } else { // ?? if (this.removeDisconnectedConnection(group)) { this.addConnectionToGroup(conn, group, maxConnCount); } else { // log.warn("(" + conn.getRemoteSocketAddress() + ")" + maxConnCount + ""); this.closeConnectionWithoutReconnect(conn); } } } } } } private void closeConnectionWithoutReconnect(final DefaultConnection conn) { try { conn.close(false); } catch (final NotifyRemotingException e) { log.error("", e); } } private void notifyConnectionReady(final DefaultConnection conn) { // ? if (conn != null) { synchronized (conn) { conn.setReady(true); conn.notifyAll(); } // ? for (final ConnectionLifeCycleListener listener : this.remotingContext.connectionLifeCycleListenerList) { try { listener.onConnectionReady(conn); } catch (final Throwable t) { log.error("ConnectionLifeCycleListener.onConnectionReady", t); } } } } private boolean removeDisconnectedConnection(final String group) { // ???connection??) final List<Connection> currentConnList = this.remotingController.getRemotingContext() .getConnectionsByGroup(group); Connection disconnectedConn = null; if (currentConnList != null) { synchronized (currentConnList) { final ListIterator<Connection> it = currentConnList.listIterator(); while (it.hasNext()) { final Connection currentConn = it.next(); if (!currentConn.isConnected()) { disconnectedConn = currentConn; break; } else { // ????? // ??? if (!((DefaultConnection) currentConn).isReady() && !currentConn.getGroupSet().isEmpty()) { this.notifyConnectionReady((DefaultConnection) currentConn); } } } } } if (disconnectedConn != null) { return currentConnList.remove(disconnectedConn); } else { return false; } } private void addConnectionToGroup(final DefaultConnection conn, final String group, final int maxConnCount) { conn.getRemotingContext().addConnectionToGroup(group, conn); // ?? final Object readyLock = this.remotingController.getAttribute(group, Constants.GROUP_CONNECTION_READY_LOCK); if (readyLock != null) { // synchronized (readyLock) { if (this.remotingController.getConnectionCount(group) >= maxConnCount) { readyLock.notifyAll(); } } } } public void onSessionCreated(final Session session) { log.debug("?:" + RemotingUtils.getAddrString(session.getRemoteSocketAddress())); final DefaultConnection connection = new DefaultConnection((NioSession) session, this.remotingContext); // this.remotingContext.addConnection(connection); // sessionconnection this.remotingContext.addSession2ConnectionMapping((NioSession) session, connection); this.remotingContext.notifyConnectionCreated(connection); this.adjustMaxScheduleWrittenBytes(); } private void adjustMaxScheduleWrittenBytes() { // Server?????? if (this.remotingController instanceof RemotingServer) { final List<Connection> connections = this.remotingContext .getConnectionsByGroup(Constants.DEFAULT_GROUP); final int connectionCount = connections != null ? connections.size() : 0; if (connectionCount > 0) { this.remotingContext.getConfig() .setMaxScheduleWrittenBytes(Runtime.getRuntime().maxMemory() / 3 / connectionCount); } } } public void onSessionExpired(final Session session) { } public void onSessionIdle(final Session session) { final Connection conn = this.remotingContext.getConnectionBySession((NioSession) session); try { conn.send(conn.getRemotingContext().getCommandFactory().createHeartBeatCommand(), new HeartBeatListener(conn), 5000, TimeUnit.MILLISECONDS); } catch (final NotifyRemotingException e) { log.error("??", e); } } public void onSessionStarted(final Session session) { } }