/* * (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.taobao.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.taobao.gecko.core.command.Constants; import com.taobao.gecko.core.command.RequestCommand; import com.taobao.gecko.core.command.ResponseCommand; import com.taobao.gecko.core.command.ResponseStatus; import com.taobao.gecko.core.command.kernel.HeartBeatRequestCommand; import com.taobao.gecko.core.core.Handler; import com.taobao.gecko.core.core.Session; import com.taobao.gecko.core.nio.NioSession; import com.taobao.gecko.core.nio.impl.TimerRef; import com.taobao.gecko.core.util.ExceptionMonitor; import com.taobao.gecko.core.util.RemotingUtils; import com.taobao.gecko.service.Connection; import com.taobao.gecko.service.ConnectionLifeCycleListener; import com.taobao.gecko.service.RemotingController; import com.taobao.gecko.service.RemotingServer; import com.taobao.gecko.service.RequestProcessor; import com.taobao.gecko.service.SingleRequestCallBackListener; import com.taobao.gecko.service.exception.IllegalMessageException; import com.taobao.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); } } private 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") private <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(); // �Ƴ�session��connectionӳ�� 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 { // �����Ѿ����رգ�����groupSetΪ�գ���ر�session���������� 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 { // ��ǰ�������ӣ�ȷ���Ѿ��Ǿ���״̬������Ϊ�˷�ֹ���г����� // ���ӽ����ɹ������dz����˹涨�ij�ʱʱ�䣬ȴ��Ȼ�������˷��飬û��֪ͨ���� 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); // ����session��connection��ӳ�� 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) { } }