package com.github.ltsopensource.core.registry;
import com.github.ltsopensource.core.AppContext;
import com.github.ltsopensource.core.cluster.Node;
import com.github.ltsopensource.core.commons.concurrent.ConcurrentHashSet;
import com.github.ltsopensource.core.commons.utils.Callable;
import com.github.ltsopensource.core.constant.Constants;
import com.github.ltsopensource.core.constant.ExtConfig;
import com.github.ltsopensource.core.factory.NamedThreadFactory;
import com.github.ltsopensource.core.support.NodeShutdownHook;
import java.util.*;
import java.util.concurrent.*;
/**
* @author Robert HG (254963746@qq.com) on 5/17/15.
*/
public abstract class FailbackRegistry extends AbstractRegistry {
// 定时任务执行器
private final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(1,
new NamedThreadFactory("LTSRegistryFailedRetryTimer", true));
// 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
private ScheduledFuture<?> retryFuture;
// 注册失败的定时重试
private final Set<Node> failedRegistered = new ConcurrentHashSet<Node>();
private final Set<Node> failedUnRegistered = new ConcurrentHashSet<Node>();
private final ConcurrentMap<Node, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<Node, Set<NotifyListener>>();
private final ConcurrentMap<Node, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<Node, Set<NotifyListener>>();
private final ConcurrentMap<Node, Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>> failedNotified = new ConcurrentHashMap<Node, Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>>();
public FailbackRegistry(AppContext appContext) {
super(appContext);
int retryPeriod = appContext.getConfig().getParameter(ExtConfig.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
public void run() {
// 检测并连接注册中心
try {
retry();
} catch (Throwable t) { // 防御性容错
LOGGER.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
}
}
}, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
NodeShutdownHook.registerHook(appContext, this.getClass().getName(), new Callable() {
@Override
public void call() throws Exception {
retryFuture.cancel(true);
retryExecutor.shutdownNow();
destroy();
}
});
}
@Override
public void register(Node node) {
try {
super.register(node);
failedRegistered.clear();
doRegister(node);
} catch (Exception e) {
// 将失败的注册请求记录到失败列表,定时重试
failedRegistered.add(node);
}
}
@Override
public void unregister(Node node) {
try {
super.unregister(node);
failedUnRegistered.clear();
doUnRegister(node);
} catch (Exception e) {
// 将失败的取消注册请求记录到失败列表,定时重试
failedUnRegistered.add(node);
}
}
@Override
public void subscribe(Node node, NotifyListener listener) {
try {
super.subscribe(node, listener);
removeFailedSubscribed(node, listener);
doSubscribe(node, listener);
} catch (Exception e) {
addFailedSubscribed(node, listener);
}
}
@Override
public void unsubscribe(Node node, NotifyListener listener) {
try {
super.unsubscribe(node, listener);
removeFailedSubscribed(node, listener);
doUnsubscribe(node, listener);
} catch (Exception e) {
addFailedUnsubscribed(node, listener);
}
}
protected void addFailedUnsubscribed(Node node, NotifyListener listener) {
// 将失败的取消订阅请求记录到失败列表,定时重试
Set<NotifyListener> listeners = failedUnsubscribed.get(node);
if (listeners == null) {
failedUnsubscribed.putIfAbsent(node, new ConcurrentHashSet<NotifyListener>());
listeners = failedUnsubscribed.get(node);
}
listeners.add(listener);
}
@Override
protected void notify(NotifyEvent event, List<Node> nodes, NotifyListener listener) {
try {
super.notify(event, nodes, listener);
} catch (Exception e) {
// 将失败的通知请求记录到失败列表,定时重试
Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>> listeners = failedNotified.get(getNode());
if (listeners == null) {
failedNotified.putIfAbsent(getNode(), new ConcurrentHashMap<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>());
listeners = failedNotified.get(getNode());
}
listeners.put(listener, new NotifyPair<NotifyEvent, List<Node>>(event, nodes));
LOGGER.error("Failed to notify, waiting for retry, cause: " + e.getMessage(), e);
}
}
@Override
public void destroy() {
super.destroy();
try {
retryFuture.cancel(true);
} catch (Throwable t) {
LOGGER.warn(t.getMessage(), t);
}
}
@Override
protected void recover() throws Exception {
// register
Set<Node> recoverRegistered = new HashSet<Node>(getRegistered());
if (!recoverRegistered.isEmpty()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Recover register node " + recoverRegistered);
}
for (Node node : recoverRegistered) {
failedRegistered.add(node);
}
}
// subscribe
Map<Node, Set<NotifyListener>> recoverSubscribed = new HashMap<Node, Set<NotifyListener>>(getSubscribed());
if (!recoverSubscribed.isEmpty()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Recover subscribe node " + recoverSubscribed.keySet());
}
for (Map.Entry<Node, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {
Node node = entry.getKey();
for (NotifyListener listener : entry.getValue()) {
addFailedSubscribed(node, listener);
}
}
}
}
private void removeFailedSubscribed(Node node, NotifyListener listener) {
Set<NotifyListener> listeners = failedSubscribed.get(node);
if (listeners != null) {
listeners.remove(listener);
}
listeners = failedUnsubscribed.get(node);
if (listeners != null) {
listeners.remove(listener);
}
Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>> notified = failedNotified.get(node);
if (notified != null) {
notified.remove(listener);
}
}
private void addFailedSubscribed(Node node, NotifyListener listener) {
Set<NotifyListener> listeners = failedSubscribed.get(node);
if (listeners == null) {
failedSubscribed.putIfAbsent(node, new ConcurrentHashSet<NotifyListener>());
listeners = failedSubscribed.get(node);
}
listeners.add(listener);
}
protected void retry() {
if (!failedRegistered.isEmpty()) {
Set<Node> failed = new HashSet<Node>(failedRegistered);
if (failed.size() > 0) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Retry register {}", failed);
}
try {
for (Node node : failed) {
doRegister(node);
failedRegistered.remove(node);
}
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
if (!failedUnRegistered.isEmpty()) {
Set<Node> failed = new HashSet<Node>(failedUnRegistered);
if (failed.size() > 0) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Retry unregister {}", failed);
}
try {
for (Node node : failed) {
doUnRegister(node);
failedUnRegistered.remove(node);
}
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry unregister " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
if (!failedSubscribed.isEmpty()) {
Map<Node, Set<NotifyListener>> failed = new HashMap<Node, Set<NotifyListener>>(failedSubscribed);
for (Map.Entry<Node, Set<NotifyListener>> entry : new HashMap<Node, Set<NotifyListener>>(failed).entrySet()) {
if (entry.getValue() == null || entry.getValue().size() == 0) {
failed.remove(entry.getKey());
}
}
if (failed.size() > 0) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Retry subscribe " + failed);
}
try {
for (Map.Entry<Node, Set<NotifyListener>> entry : failed.entrySet()) {
Node node = entry.getKey();
Set<NotifyListener> listeners = entry.getValue();
for (NotifyListener listener : listeners) {
try {
doSubscribe(node, listener);
listeners.remove(listener);
failedSubscribed.remove(entry.getKey());
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
if (!failedUnsubscribed.isEmpty()) {
Map<Node, Set<NotifyListener>> failed = new HashMap<Node, Set<NotifyListener>>(failedUnsubscribed);
for (Map.Entry<Node, Set<NotifyListener>> entry : new HashMap<Node, Set<NotifyListener>>(failed).entrySet()) {
if (entry.getValue() == null || entry.getValue().size() == 0) {
failed.remove(entry.getKey());
}
}
if (failed.size() > 0) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Retry unsubscribe " + failed);
}
try {
for (Map.Entry<Node, Set<NotifyListener>> entry : failed.entrySet()) {
Node node = entry.getKey();
Set<NotifyListener> listeners = entry.getValue();
for (NotifyListener listener : listeners) {
try {
doUnsubscribe(node, listener);
listeners.remove(listener);
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
if (!failedNotified.isEmpty()) {
Map<Node, Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>> failed = new HashMap<Node, Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>>(failedNotified);
for (Map.Entry<Node, Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>> entry : new HashMap<Node, Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>>>(failed).entrySet()) {
if (entry.getValue() == null || entry.getValue().size() == 0) {
failed.remove(entry.getKey());
}
}
if (failed.size() > 0) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Retry notify " + failed);
}
try {
for (Map<NotifyListener, NotifyPair<NotifyEvent, List<Node>>> values : failed.values()) {
for (Map.Entry<NotifyListener, NotifyPair<NotifyEvent, List<Node>>> entry : values.entrySet()) {
try {
NotifyListener listener = entry.getKey();
NotifyPair<NotifyEvent, List<Node>> notifyPair = entry.getValue();
listener.notify(notifyPair.event, notifyPair.nodes);
values.remove(listener);
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
} catch (Throwable t) { // 忽略所有异常,等待下次重试
LOGGER.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
}
}
}
}
private class NotifyPair<T1, T2> {
T1 event;
T2 nodes;
public NotifyPair(T1 event, T2 nodes) {
this.event = event;
this.nodes = nodes;
}
}
protected abstract void doRegister(Node node);
protected abstract void doUnRegister(Node node);
protected abstract void doSubscribe(Node node, NotifyListener listener);
protected abstract void doUnsubscribe(Node node, NotifyListener listener);
}