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.taobao.gecko.service.impl; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.taobao.gecko.core.command.Constants; import com.taobao.gecko.core.extension.GeckoTCPConnectorController; import com.taobao.gecko.core.nio.NioSession; import com.taobao.gecko.core.nio.impl.TimerRef; import com.taobao.gecko.core.util.ConcurrentHashSet; import com.taobao.gecko.core.util.RemotingUtils; import com.taobao.gecko.service.config.ClientConfig; import com.taobao.gecko.service.exception.NotifyRemotingException; /** * * * * @author boyan * * @since 1.0, 2009-12-15 03:01:38 */ public class ReconnectManager { /** * */ private final LinkedBlockingQueue<ReconnectTask> tasks = new LinkedBlockingQueue<ReconnectTask>(); /** * */ private final ConcurrentHashSet<String/* group */> canceledGroupSet = new ConcurrentHashSet<String>(); private volatile boolean started = false; private final GeckoTCPConnectorController connector; private static final Log log = LogFactory.getLog(ReconnectManager.class); private final ClientConfig clientConfig; private final DefaultRemotingClient remotingClient; private int maxRetryTimes = -1; /** * */ private final Thread[] healConnectionThreads; private final class HealConnectionRunner implements Runnable { private long lastConnectTime = -1; // @Override public void run() { while (ReconnectManager.this.started) { long start = -1; ReconnectTask task = null; try { // sleep if (this.lastConnectTime > 0 && this.lastConnectTime < ReconnectManager.this.clientConfig.getHealConnectionInterval() || this.lastConnectTime < 0) { Thread.sleep(ReconnectManager.this.clientConfig.getHealConnectionInterval()); } task = ReconnectManager.this.tasks.take(); // final Set<String> copySet = new HashSet<String>(task.getGroupSet()); // copySet.remove(Constants.DEFAULT_GROUP); start = System.currentTimeMillis(); if (ReconnectManager.this.isValidTask(task)) { this.doReconnectTask(task); } else { log.warn("Invalid reconnect request,the group set is:" + copySet); } this.lastConnectTime = System.currentTimeMillis() - start; } catch (final InterruptedException e) { // ignorestarted } catch (final Exception e) { if (start != -1) { this.lastConnectTime = System.currentTimeMillis() - start; } if (task != null) { log.error("Reconnect to " + RemotingUtils.getAddrString(task.getRemoteAddress()) + "", e.getCause()); this.readdTask(task); } } } } private void readdTask(ReconnectTask task) { if (ReconnectManager.this.maxRetryTimes <= 0 || task.increaseRetryCounterAndGet() < ReconnectManager.this.maxRetryTimes) { ReconnectManager.this.addReconnectTask(task); } else { log.warn("Retry too many times to reconnect to " + RemotingUtils.getAddrString(task.getRemoteAddress()) + ",we will remove the task."); } } private void doReconnectTask(final ReconnectTask task) throws IOException, NotifyRemotingException { log.info("Try to reconnect to " + RemotingUtils.getAddrString(task.getRemoteAddress())); final TimerRef timerRef = new TimerRef(ReconnectManager.this.clientConfig.getConnectTimeout(), null); try { final Future<NioSession> future = ReconnectManager.this.connector.connect(task.getRemoteAddress(), task.getGroupSet(), task.getRemoteAddress(), timerRef); final DefaultRemotingClient.CheckConnectFutureRunner runnable = new DefaultRemotingClient.CheckConnectFutureRunner( future, task.getRemoteAddress(), task.getGroupSet(), ReconnectManager.this.remotingClient); timerRef.setRunnable(runnable); ReconnectManager.this.remotingClient.insertTimer(timerRef); // task.setDone(true); } catch (final Exception e) { this.readdTask(task); } } } public ReconnectManager(final GeckoTCPConnectorController connector, final ClientConfig clientConfig, final DefaultRemotingClient remotingClient) { super(); this.connector = connector; this.clientConfig = clientConfig; this.remotingClient = remotingClient; this.started = true; this.maxRetryTimes = clientConfig.getMaxReconnectTimes(); this.healConnectionThreads = new Thread[this.clientConfig.getHealConnectionExecutorPoolSize()]; } public synchronized void start() { for (int i = 0; i < this.clientConfig.getHealConnectionExecutorPoolSize(); i++) { this.healConnectionThreads[i] = new Thread(new HealConnectionRunner()); this.healConnectionThreads[i].start(); } } public int getReconnectTaskCount() { return this.tasks.size(); } public void addReconnectTask(final ReconnectTask task) { if (!this.isValidTask(task)) { log.warn("Invalid reconnect request,it is removed,the group set is:" + task.getGroupSet()); return; } this.tasks.offer(task); } boolean isValidTask(final ReconnectTask task) { task.getGroupSet().removeAll(this.canceledGroupSet); return this.isValidGroup(task) && !task.isDone(); } /** * * * @param task * @return */ boolean isValidGroup(final ReconnectTask task) { return !this.hasOnlyDefaultGroup(task) && !this.isEmptyGroupSet(task); } /** * * * @param task * @return */ private boolean isEmptyGroupSet(final ReconnectTask task) { return task.getGroupSet().size() == 0; } /** * * * @param task * @return */ private boolean hasOnlyDefaultGroup(final ReconnectTask task) { return task.getGroupSet().size() == 1 && task.getGroupSet().contains(Constants.DEFAULT_GROUP); } public void removeCanceledGroup(final String group) { this.canceledGroupSet.remove(group); } public void cancelReconnectGroup(final String group) { this.canceledGroupSet.add(group); final Iterator<ReconnectTask> it = this.tasks.iterator(); while (it.hasNext()) { final ReconnectTask task = it.next(); if (task.getGroupSet().contains(group)) { log.warn("Invalid reconnect request,it is removed,the group set is:" + task.getGroupSet()); it.remove(); } } } public synchronized void stop() { if (!this.started) { return; } this.started = false; for (final Thread thread : this.healConnectionThreads) { thread.interrupt(); } this.tasks.clear(); this.canceledGroupSet.clear(); } }