registry ..
diff --git a/README.md b/README.md
index cc8612e..03110cd 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@
   
   When the user is managing changes in the background,
   
-  Soul dynamically updates the cache by listening to the zookeeper node, websocket push,http longPull.
+  Soul dynamically updates the cache by listening to the zookeeper ttlCacheTimerTask, websocket push,http longPull.
   
   ![Data Sync](https://bestkobe.gitee.io/images/soul/soul-config-processor.png?_t=201908032316)
   
diff --git a/soul-common/src/main/java/org/dromara/soul/common/cache/HashedWheelTimer.java b/soul-common/src/main/java/org/dromara/soul/common/cache/HashedWheelTimer.java
deleted file mode 100644
index 20a23bb..0000000
--- a/soul-common/src/main/java/org/dromara/soul/common/cache/HashedWheelTimer.java
+++ /dev/null
@@ -1,657 +0,0 @@
-/*
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements.See the NOTICE file distributed with
- *   this work for additional information regarding copyright ownership.
- *   The ASF licenses this file to You 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 org.dromara.soul.common.cache;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
-import org.dromara.soul.common.utils.OsUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Loop time implementation.
- * io.netty.util.HashedWheelTimer
- *
- * @author sixh
- */
-public class HashedWheelTimer implements Timer {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(HashedWheelTimer.class);
-
-    /**
-     * 状态更新.
-     */
-    private static final AtomicIntegerFieldUpdater<HashedWheelTimer> WORKER_STATE_UPDATER;
-
-
-    /**
-     * 任务执行状态.
-     */
-    private static final int WORKER_STATE_INIT = 0;
-
-    /**
-     * 任务已启动.
-     */
-    private static final int WORKER_STATE_STARTED = 1;
-
-    /**
-     * 任务已结束.
-     */
-    private static final int WORKER_STATE_SHUTDOWN = 2;
-
-    /**
-     * 时间分片槽,默认512.
-     */
-    private final HashedWheelTimer.HashedWheelBucket[] wheel;
-
-    /**
-     * 执行时间分片槽的次数; {@link #wheel}长度 -1.
-     */
-    private final int mask;
-
-    /**
-     * 时间分片执行时间.
-     */
-    private final long tickDuration;
-
-    /**
-     * 执行线程.
-     */
-    private final Thread workerThread;
-
-    /**
-     * 任务执行实现.
-     */
-    private final HashedWheelTimer.Worker worker = new HashedWheelTimer.Worker();
-
-    /**
-     * 开始时间.
-     */
-    private volatile long startTime;
-
-    /**
-     * 开始时间初始化.
-     */
-    private final CountDownLatch startTimeInitialized = new CountDownLatch(1);
-
-
-    /**
-     * 超时器.
-     */
-    private final Queue<HashedWheelTimeout> timeouts = new ConcurrentLinkedQueue<>();
-
-    /**
-     * 取消超时器.
-     */
-    private final Queue<HashedWheelTimeout> cancelledTimeouts = new ConcurrentLinkedQueue<>();
-
-    static {
-        WORKER_STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimer.class, "workerState");
-    }
-
-    /**
-     * 0 - init, 1 - started, 2 - shut down.
-     */
-    @SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization"})
-    private volatile int workerState = WORKER_STATE_INIT;
-
-    /**
-     * 初始化一个环形时间执行器.
-     *
-     * @param threadFactory 线程初始化工厂;
-     */
-    HashedWheelTimer(ThreadFactory threadFactory) {
-        this(threadFactory, 100, TimeUnit.MILLISECONDS, 512);
-    }
-
-    /**
-     * 初始化一个环形时间执行器.
-     *
-     * @param threadFactory 线程初始化工厂;
-     * @param tickDuration  持续执行时间;
-     * @param unit          时间单位;
-     * @param ticksPerWheel 时间分片数;
-     */
-    private HashedWheelTimer(ThreadFactory threadFactory,
-                             long tickDuration, TimeUnit unit, int ticksPerWheel) {
-        /*
-         * 创建时间分片区;
-         */
-        this.wheel = createWheel(ticksPerWheel);
-        //执行时间分片槽的次数;
-        mask = wheel.length - 1;
-        //把所有时间转为纳秒进方便操作;
-        this.tickDuration = unit.toNanos(tickDuration);
-        // Prevent overflow.
-        if (this.tickDuration >= Long.MAX_VALUE / wheel.length) {
-            throw new IllegalArgumentException(String.format(
-                    "tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
-                    tickDuration, Long.MAX_VALUE / wheel.length));
-        }
-        workerThread = threadFactory.newThread(worker);
-    }
-
-    /**
-     * 启动任务.
-     */
-    private void start() {
-        switch (WORKER_STATE_UPDATER.get(this)) {
-            //更改线程状态;如果当前的状态是初始,则改成启动中;
-            case WORKER_STATE_INIT:
-                if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
-                    //启动线程;
-                    workerThread.start();
-                }
-                break;
-            case WORKER_STATE_STARTED:
-                break;
-            case WORKER_STATE_SHUTDOWN:
-                throw new IllegalStateException("cannot be started once stopped");
-            default:
-                throw new Error("Invalid WorkerState");
-        }
-
-        // 等待初始化的Worker线程的startTime实始化;
-        while (startTime == 0) {
-            try {
-                startTimeInitialized.await();
-            } catch (InterruptedException ignore) {
-                // Ignore - it will be ready very soon.
-            }
-        }
-    }
-
-    private static HashedWheelTimer.HashedWheelBucket[] createWheel(int ticksPerWheel) {
-        int newTicksPerWheel = ticksPerWheel;
-        if (newTicksPerWheel <= 0) {
-            throw new IllegalArgumentException(
-                    "ticksPerWheel must be greater than 0: " + newTicksPerWheel);
-        }
-        if (newTicksPerWheel > 1073741824) {
-            throw new IllegalArgumentException(
-                    "ticksPerWheel may not be greater than 2^30: " + newTicksPerWheel);
-        }
-
-        newTicksPerWheel = normalizeTicksPerWheel(newTicksPerWheel);
-        HashedWheelTimer.HashedWheelBucket[] wheel = new HashedWheelTimer.HashedWheelBucket[newTicksPerWheel];
-        for (int i = 0; i < wheel.length; i++) {
-            wheel[i] = new HashedWheelTimer.HashedWheelBucket();
-        }
-        return wheel;
-    }
-
-    private static int normalizeTicksPerWheel(int ticksPerWheel) {
-        int normalizedTicksPerWheel = 1;
-        while (normalizedTicksPerWheel < ticksPerWheel) {
-            normalizedTicksPerWheel <<= 1;
-        }
-        return normalizedTicksPerWheel;
-    }
-
-    @Override
-    public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
-        if (task == null) {
-            throw new NullPointerException("task");
-        }
-        if (unit == null) {
-            throw new NullPointerException("unit");
-        }
-        start();
-        /*
-         * 设置一下超时作务到列队中;计算出下一次的超时时间;
-         */
-        long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
-        HashedWheelTimer.HashedWheelTimeout timeout = new HashedWheelTimer.HashedWheelTimeout(this, task, deadline);
-        timeouts.add(timeout);
-        return timeout;
-    }
-
-    @Override
-    public Set<Timeout> stop() {
-        if (Thread.currentThread() == workerThread) {
-            throw new IllegalStateException(
-                    HashedWheelTimer.class.getSimpleName() +
-                    ".stop() cannot be called from " +
-                    TimerTask.class.getSimpleName());
-        }
-
-        if (!WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_STARTED, WORKER_STATE_SHUTDOWN)) {
-            // workerState can be 0 or 2 at this moment - let it always be 2.
-            WORKER_STATE_UPDATER.set(this, WORKER_STATE_SHUTDOWN);
-            return Collections.emptySet();
-        }
-
-        boolean interrupted = false;
-        while (workerThread.isAlive()) {
-            workerThread.interrupt();
-            try {
-                workerThread.join(100);
-            } catch (InterruptedException ignored) {
-                interrupted = true;
-            }
-        }
-
-        if (interrupted) {
-            Thread.currentThread().interrupt();
-        }
-
-        return worker.unprocessedTimeouts();
-    }
-
-    private final class Worker implements Runnable {
-        private final Set<Timeout> unprocessedTimeouts = new HashSet<>();
-
-        /**
-         * 到达一个槽就是++.
-         */
-        private long tick;
-
-        @Override
-        public void run() {
-            //初始化启动开始时间;
-            startTime = System.nanoTime();
-            if (startTime == 0) {
-                // 因为startTime使用0来判断是否进行初始化,所以要保证他的初始化不为0;
-                startTime = 1;
-            }
-            //结束执行start()方法;
-            startTimeInitialized.countDown();
-
-            do {
-                //计算下一个到达的时间片;如果没有到达则需要等待;
-                final long deadline = waitForNextTick();
-                if (deadline > 0) {
-                    int idx = (int) (tick & mask);
-                    processCancelledTasks();
-                    //取出一个时间分片槽
-                    HashedWheelTimer.HashedWheelBucket bucket =
-                            wheel[idx];
-                    //转换时间分片槽。并将数据放入到时间分片槽上;
-                    transferTimeoutsToBuckets();
-                    bucket.expireTimeouts(deadline);
-                    tick++;
-                }
-            } while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
-
-            // 如果调用Stop方法,就会填充未超时的Task将他返回给用户;
-            for (HashedWheelTimer.HashedWheelBucket bucket : wheel) {
-                bucket.clearTimeouts(unprocessedTimeouts);
-            }
-            for (; ; ) {
-                HashedWheelTimer.HashedWheelTimeout timeout = timeouts.poll();
-                if (timeout == null) {
-                    break;
-                }
-                if (!timeout.isCancelled()) {
-                    unprocessedTimeouts.add(timeout);
-                }
-            }
-            processCancelledTasks();
-        }
-
-        private void transferTimeoutsToBuckets() {
-            // transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just
-            // adds new timeouts in a loop.
-            for (int i = 0; i < 100000; i++) {
-                //获取一个作务;
-                HashedWheelTimer.HashedWheelTimeout timeout = timeouts.poll();
-                if (timeout == null) {
-                    // all processed
-                    break;
-                }
-                if (timeout.state() == HashedWheelTimer.HashedWheelTimeout.ST_CANCELLED) {
-                    // Was cancelled in the meantime.
-                    continue;
-                }
-                //需要轮转多少次,就可以到达时间分片上,(超时时间/任务分片轮转时间)
-                long calculated = timeout.deadline / tickDuration;
-                //计算还有剩余多少轮没有执行;
-                timeout.remainingRounds = (calculated - tick) / wheel.length;
-                // Ensure we don't schedule for past.
-                final long ticks = Math.max(calculated, tick);
-                //算出任务应该插入的时间分片槽位置;
-                int stopIndex = (int) (ticks & mask);
-                HashedWheelTimer.HashedWheelBucket bucket = wheel[stopIndex];
-                bucket.addTimeout(timeout);
-            }
-        }
-
-        /**
-         * 流程任务取消;从取消队列中获取任务。并删除.
-         */
-        private void processCancelledTasks() {
-            for (; ; ) {
-                HashedWheelTimer.HashedWheelTimeout timeout = cancelledTimeouts.poll();
-                if (timeout == null) {
-                    // all processed
-                    break;
-                }
-                try {
-                    timeout.remove();
-                } catch (Throwable t) {
-                    if (LOGGER.isWarnEnabled()) {
-                        LOGGER.warn("An exception was thrown while process a cancellation task", t);
-                    }
-                }
-            }
-        }
-
-        /**
-         * calculate goal nanoTime from startTime and current tick number,
-         * then wait until that goal has been reached.
-         *
-         * @return Long.MIN_VALUE if received a shutdown request,
-         * current time otherwise (with Long.MIN_VALUE changed by +1).
-         */
-        private long waitForNextTick() {
-            //计算时间片;例:1000000*(1+1)
-            long deadline = tickDuration * (tick + 1);
-
-            for (; ; ) {
-                //得到当前已经耗时;
-                final long currentTime = System.nanoTime() - startTime;
-                //得到休眠的时间毫秒数;
-                long sleepTimeMs = (deadline - currentTime + 999999) / 1000000;
-
-                if (sleepTimeMs <= 0) {
-                    if (currentTime == Long.MIN_VALUE) {
-                        return -Long.MAX_VALUE;
-                    } else {
-                        return currentTime;
-                    }
-                }
-
-                // Check if we run on windows, as if thats the case we will need
-                // to round the sleepTime as workaround for a bug that only affect
-                // the JVM if it runs on windows.
-                //
-                // See https://github.com/netty/netty/issues/356
-                if (OsUtils.isWindows()) {
-                    sleepTimeMs = sleepTimeMs / 10 * 10;
-                }
-
-                try {
-                    TimeUnit.MILLISECONDS.sleep(sleepTimeMs);
-                } catch (InterruptedException ignored) {
-                    if (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_SHUTDOWN) {
-                        return Long.MIN_VALUE;
-                    }
-                }
-            }
-        }
-
-        Set<Timeout> unprocessedTimeouts() {
-            return Collections.unmodifiableSet(unprocessedTimeouts);
-        }
-    }
-
-    /**
-     * 超时任务对象.
-     */
-    private static final class HashedWheelTimeout implements Timeout {
-
-        private static final int ST_INIT = 0;
-
-        private static final int ST_CANCELLED = 1;
-
-        private static final int ST_EXPIRED = 2;
-
-        private static final AtomicIntegerFieldUpdater<HashedWheelTimeout> STATE_UPDATER;
-
-        static {
-            AtomicIntegerFieldUpdater<HashedWheelTimeout> updater =
-                    AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimer.HashedWheelTimeout.class, "state");
-            STATE_UPDATER = updater;
-        }
-
-        private final HashedWheelTimer timer;
-
-        private final TimerTask task;
-
-        private final long deadline;
-
-        @SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization"})
-        private volatile int state = ST_INIT;
-
-        /**
-         * remainingRounds will be calculated and set by Worker.transferTimeoutsToBuckets() before the
-         * HashedWheelTimeout will be added to the correct HashedWheelBucket.
-         */
-        private long remainingRounds;
-
-        /**
-         * This will be used to chain timeouts in HashedWheelTimerBucket via a double-linked-list.
-         * As only the workerThread will act on it there is no need for synchronization / volatile.
-         */
-        private HashedWheelTimer.HashedWheelTimeout next;
-
-        private HashedWheelTimer.HashedWheelTimeout prev;
-
-        /**
-         * The bucket to which the timeout was added
-         */
-        private HashedWheelTimer.HashedWheelBucket bucket;
-
-        HashedWheelTimeout(HashedWheelTimer timer, TimerTask task, long deadline) {
-            this.timer = timer;
-            this.task = task;
-            this.deadline = deadline;
-        }
-
-        @Override
-        public Timer timer() {
-            return timer;
-        }
-
-        @Override
-        public TimerTask task() {
-            return task;
-        }
-
-        @Override
-        public long deadline() {
-            return deadline;
-        }
-
-        @Override
-        public boolean cancel() {
-            // only update the state it will be removed from HashedWheelBucket on next tick.
-            if (!compareAndSetState(ST_INIT, ST_CANCELLED)) {
-                return false;
-            }
-            // If a task should be canceled we put this to another queue which will be processed on each tick.
-            // So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way
-            // we can make again use of our MpscLinkedQueue and so minimize the locking / overhead as much as possible.
-            timer.cancelledTimeouts.add(this);
-            return true;
-        }
-
-        void remove() {
-            HashedWheelTimer.HashedWheelBucket bucket = this.bucket;
-            if (bucket != null) {
-                bucket.remove(this);
-            }
-        }
-
-        boolean compareAndSetState(int expected, int state) {
-            return STATE_UPDATER.compareAndSet(this, expected, state);
-        }
-
-        int state() {
-            return state;
-        }
-
-        @Override
-        public boolean isCancelled() {
-            return state() == ST_CANCELLED;
-        }
-
-        @Override
-        public boolean isExpired() {
-            return state() == ST_EXPIRED;
-        }
-
-        void expire() {
-            if (!compareAndSetState(ST_INIT, ST_EXPIRED)) {
-                return;
-            }
-
-            try {
-                task.run(this);
-            } catch (Throwable t) {
-                if (LOGGER.isWarnEnabled()) {
-                    LOGGER.warn("An exception was thrown by " + TimerTask.class.getSimpleName() + '.', t);
-                }
-            }
-        }
-
-        @Override
-        public boolean isDefault() {
-            return false;
-        }
-    }
-
-    /**
-     * 每个分片槽封装对象,
-     * 这里使用的是双向队列.
-     */
-    private static final class HashedWheelBucket {
-        private HashedWheelTimeout head;
-
-        private HashedWheelTimeout tail;
-
-        /**
-         * Add {@link HashedWheelTimeout} to this bucket.
-         */
-        void addTimeout(HashedWheelTimeout timeout) {
-            assert timeout.bucket == null;
-            timeout.bucket = this;
-            if (head == null) {
-                head = tail = timeout;
-            } else {
-                tail.next = timeout;
-                timeout.prev = tail;
-                tail = timeout;
-            }
-        }
-
-        /**
-         * Expire all {@link HashedWheelTimeout}s for the given {@code deadline}.
-         */
-        void expireTimeouts(long deadline) {
-            HashedWheelTimeout timeout = head;
-            // process all timeouts
-            while (timeout != null) {
-                boolean remove = false;
-                if (timeout.remainingRounds <= 0) {
-                    if (timeout.deadline <= deadline) {
-                        timeout.expire();
-                    } else {
-                        // The timeout was placed into a wrong slot. This should never happen.
-                        throw new IllegalStateException(String.format(
-                                "timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline));
-                    }
-                    remove = true;
-                } else if (timeout.isCancelled()) {
-                    remove = true;
-                } else {
-                    timeout.remainingRounds--;
-                }
-                // store reference to next as we may null out timeout.next in the remove block.
-                HashedWheelTimeout next = timeout.next;
-                if (remove) {
-                    remove(timeout);
-                }
-                timeout = next;
-            }
-        }
-
-        void remove(HashedWheelTimeout timeout) {
-            HashedWheelTimeout next = timeout.next;
-            // remove timeout that was either processed or cancelled by updating the linked-list
-            if (timeout.prev != null) {
-                timeout.prev.next = next;
-            }
-            if (timeout.next != null) {
-                timeout.next.prev = timeout.prev;
-            }
-
-            if (timeout == head) {
-                // if timeout is also the tail we need to adjust the entry too
-                if (timeout == tail) {
-                    tail = null;
-                    head = null;
-                } else {
-                    head = next;
-                }
-            } else if (timeout == tail) {
-                // if the timeout is the tail modify the tail to be the prev node.
-                tail = timeout.prev;
-            }
-            // null out prev, next and bucket to allow for GC.
-            timeout.prev = null;
-            timeout.next = null;
-            timeout.bucket = null;
-        }
-
-        /**
-         * Clear this bucket and return all not expired / cancelled {@link Timeout}s.
-         */
-        void clearTimeouts(Set<Timeout> set) {
-            for (; ; ) {
-                HashedWheelTimeout timeout = pollTimeout();
-                if (timeout == null) {
-                    return;
-                }
-                if (timeout.isExpired() || timeout.isCancelled()) {
-                    continue;
-                }
-                set.add(timeout);
-            }
-        }
-
-        private HashedWheelTimeout pollTimeout() {
-            HashedWheelTimeout head = this.head;
-            if (head == null) {
-                return null;
-            }
-            HashedWheelTimeout next = head.next;
-            if (next == null) {
-                tail = this.head = null;
-            } else {
-                this.head = next;
-                next.prev = null;
-            }
-
-            // null out prev and next to allow for GC.
-            head.next = null;
-            head.prev = null;
-            head.bucket = null;
-            return head;
-        }
-    }
-
-}
diff --git a/soul-common/src/main/java/org/dromara/soul/common/cache/Timeout.java b/soul-common/src/main/java/org/dromara/soul/common/cache/Timeout.java
deleted file mode 100644
index 52fbbaf..0000000
--- a/soul-common/src/main/java/org/dromara/soul/common/cache/Timeout.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements.See the NOTICE file distributed with
- *   this work for additional information regarding copyright ownership.
- *   The ASF licenses this file to You 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 org.dromara.soul.common.cache;
-
-/**
- * timeout.
- *
- * @author chenbinl
- * @see HashedWheelTimer;
- */
-public interface Timeout {
-
-    /**
-     * Returns the {@link Timer} that created this handle.
-     */
-    Timer timer();
-
-    /**
-     * Returns the {@link TimerTask} which is associated with this handle.
-     */
-    TimerTask task();
-
-    /**
-     * Returns {@code true} if and only if the {@link TimerTask} associated
-     * with this handle has been expired.
-     */
-    boolean isExpired();
-
-    /**
-     * Returns {@code true} if and only if the {@link TimerTask} associated
-     * with this handle has been cancelled.
-     */
-    boolean isCancelled();
-
-    /**
-     * Attempts to cancel the {@link TimerTask} associated with this handle.
-     * If the task has been executed or cancelled already, it will return with
-     * no side effect.
-     *
-     * @return True if the cancellation completed successfully, otherwise false
-     */
-    boolean cancel();
-
-    /**
-     * 是否已经最后执行期限.
-     *
-     * @return true or false.
-     */
-    long deadline();
-
-    /**
-     * 是否为自定义的一个timeout.
-     *
-     * @return true or false.
-     */
-    default boolean isDefault() {
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/soul-common/src/main/java/org/dromara/soul/common/cache/Timer.java b/soul-common/src/main/java/org/dromara/soul/common/cache/Timer.java
deleted file mode 100644
index fbe8eda..0000000
--- a/soul-common/src/main/java/org/dromara/soul/common/cache/Timer.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements.See the NOTICE file distributed with
- *   this work for additional information regarding copyright ownership.
- *   The ASF licenses this file to You 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 org.dromara.soul.common.cache;
-
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Timer.
- * @author chenbin
- */
-public interface Timer {
-
-    /**
-     * Schedules the specified {@link TimerTask} for one-time execution after
-     * the specified delay.
-     * @param task timeTask
-     * @param delay delay time;
-     * @param unit time unit;
-     * @return a handle which is associated with the specified task
-     *
-     * @throws IllegalStateException if this timer has been
-     *                               {@linkplain #stop() stopped} already
-     */
-    Timeout newTimeout(TimerTask task, long delay, TimeUnit unit);
-
-    /**
-     * Releases all resources acquired by this {@link Timer} and cancels all
-     * tasks which were scheduled but not executed yet.
-     *
-     * @return the handles associated with the tasks which were canceled by
-     *         this method
-     */
-    Set<Timeout> stop();
-}
diff --git a/soul-common/src/main/java/org/dromara/soul/common/cache/TimerTask.java b/soul-common/src/main/java/org/dromara/soul/common/cache/TimerTask.java
deleted file mode 100644
index 7060f23..0000000
--- a/soul-common/src/main/java/org/dromara/soul/common/cache/TimerTask.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- *
- *   Licensed to the Apache Software Foundation (ASF) under one or more
- *   contributor license agreements.See the NOTICE file distributed with
- *   this work for additional information regarding copyright ownership.
- *   The ASF licenses this file to You 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 org.dromara.soul.common.cache;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A task which is executed after the delay specified with
- * {@link Timer#newTimeout(TimerTask, long, TimeUnit)}.
- *
- * @author chenbin
- */
-public interface TimerTask {
-
-    /**
-     * Executed after the delay specified with
-     * {@link Timer#newTimeout(TimerTask, long, TimeUnit)}.
-     *
-     * @param timeout a handle which is associated with this task
-     */
-    void run(Timeout timeout);
-}
diff --git a/soul-common/src/main/java/org/dromara/soul/common/cache/TtlCache.java b/soul-common/src/main/java/org/dromara/soul/common/cache/TtlCache.java
index b3ebac7..fb62939 100644
--- a/soul-common/src/main/java/org/dromara/soul/common/cache/TtlCache.java
+++ b/soul-common/src/main/java/org/dromara/soul/common/cache/TtlCache.java
@@ -18,8 +18,12 @@
 
 package org.dromara.soul.common.cache;
 
-import java.util.concurrent.TimeUnit;
 import org.dromara.soul.common.concurrent.SoulThreadFactory;
+import org.dromara.soul.common.timer.HashedWheelTimer;
+import org.dromara.soul.common.timer.Timeout;
+import org.dromara.soul.common.timer.TimerTask;
+
+import java.util.concurrent.TimeUnit;
 
 /**
  * 实现一个关于时间的过期的缓存;当缓存的对象到期后,
@@ -94,14 +98,14 @@
      * @return Timeout
      */
     public Timeout put(K k, V v, long expire, TimeUnit unit) {
-        Node node = new Node(k, v, expire, unit);
-        return timer.newTimeout(node, expire, unit);
+        TtlCacheTimerTask ttlCacheTimerTask = new TtlCacheTimerTask(k, v, expire, unit);
+        return timer.newTimeout(ttlCacheTimerTask, expire, unit);
     }
 
     /**
      * 保存.
      */
-    public class Node implements TimerTask {
+    public class TtlCacheTimerTask implements TimerTask {
         /**
          * 缓存的KEY.
          */
@@ -135,7 +139,7 @@
          * @param expire 默认等待时间;
          * @param unit   默认等时间的单位;
          */
-        public Node(K key, V value, long expire, TimeUnit unit) {
+        TtlCacheTimerTask(K key, V value, long expire, TimeUnit unit) {
             this.key = key;
             this.value = value;
             time = System.nanoTime();
diff --git a/soul-common/src/main/java/org/dromara/soul/common/timer/HashedWheelTimer.java b/soul-common/src/main/java/org/dromara/soul/common/timer/HashedWheelTimer.java
new file mode 100644
index 0000000..4e43b72
--- /dev/null
+++ b/soul-common/src/main/java/org/dromara/soul/common/timer/HashedWheelTimer.java
@@ -0,0 +1,829 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.dromara.soul.common.timer;
+
+
+import org.dromara.soul.common.utils.OsUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A {@link Timer} optimized for approximated I/O timeout scheduling.
+ *
+ * <h3>Tick Duration</h3>
+ * <p>
+ * As described with 'approximated', this timer does not execute the scheduled
+ * {@link TimerTask} on time.  {@link HashedWheelTimer}, on every tick, will
+ * check if there are any {@link TimerTask}s behind the schedule and execute
+ * them.
+ * <p>
+ * You can increase or decrease the accuracy of the execution timing by
+ * specifying smaller or larger tick duration in the constructor.  In most
+ * network applications, I/O timeout does not need to be accurate.  Therefore,
+ * the default tick duration is 100 milliseconds and you will not need to try
+ * different configurations in most cases.
+ *
+ * <h3>Ticks per Wheel (Wheel Size)</h3>
+ * <p>
+ * {@link HashedWheelTimer} maintains a data structure called 'wheel'.
+ * To put simply, a wheel is a hash table of {@link TimerTask}s whose hash
+ * function is 'dead line of the task'.  The default number of ticks per wheel
+ * (i.e. the size of the wheel) is 512.  You could specify a larger value
+ * if you are going to schedule a lot of timeouts.
+ *
+ * <h3>Do not create many instances.</h3>
+ * <p>
+ * {@link HashedWheelTimer} creates a new thread whenever it is instantiated and
+ * started.  Therefore, you should make sure to create only one instance and
+ * share it across your application.  One of the common mistakes, that makes
+ * your application unresponsive, is to create a new instance for every connection.
+ *
+ * <h3>Implementation Details</h3>
+ * <p>
+ * {@link HashedWheelTimer} is based on
+ * <a href="http://cseweb.ucsd.edu/users/varghese/">George Varghese</a> and
+ * Tony Lauck's paper,
+ * <a href="http://cseweb.ucsd.edu/users/varghese/PAPERS/twheel.ps.Z">'Hashed
+ * and Hierarchical Timing Wheels: data structures to efficiently implement a
+ * timer facility'</a>.  More comprehensive slides are located
+ * <a href="http://www.cse.wustl.edu/~cdgill/courses/cs6874/TimingWheels.ppt">here</a>.
+ */
+public class HashedWheelTimer implements Timer {
+
+    static final Logger logger =
+            LoggerFactory.getLogger(HashedWheelTimer.class);
+
+    private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger();
+    private static final AtomicBoolean WARNED_TOO_MANY_INSTANCES = new AtomicBoolean();
+    private static final int INSTANCE_COUNT_LIMIT = 64;
+    private static final long MILLISECOND_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
+
+
+    private static final AtomicIntegerFieldUpdater<HashedWheelTimer> WORKER_STATE_UPDATER =
+            AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimer.class, "workerState");
+
+    private final Worker worker = new Worker();
+    private final Thread workerThread;
+
+    public static final int WORKER_STATE_INIT = 0;
+    public static final int WORKER_STATE_STARTED = 1;
+    public static final int WORKER_STATE_SHUTDOWN = 2;
+    @SuppressWarnings({"unused", "FieldMayBeFinal"})
+    private volatile int workerState; // 0 - init, 1 - started, 2 - shut down
+
+    private final long tickDuration;
+    private final HashedWheelBucket[] wheel;
+    private final int mask;
+    private final CountDownLatch startTimeInitialized = new CountDownLatch(1);
+    private final Queue<HashedWheelTimeout> timeouts = new ConcurrentLinkedQueue<>();
+    private final Queue<HashedWheelTimeout> cancelledTimeouts = new ConcurrentLinkedDeque<>();
+    private final AtomicLong pendingTimeouts = new AtomicLong(0);
+    private final long maxPendingTimeouts;
+
+    private volatile long startTime;
+
+    /**
+     * Creates a new timer with the default thread factory
+     * ({@link Executors#defaultThreadFactory()}), default tick duration, and
+     * default number of ticks per wheel.
+     */
+    public HashedWheelTimer() {
+        this(Executors.defaultThreadFactory());
+    }
+
+    /**
+     * Creates a new timer with the default thread factory
+     * ({@link Executors#defaultThreadFactory()}) and default number of ticks
+     * per wheel.
+     *
+     * @param tickDuration the duration between tick
+     * @param unit         the time unit of the {@code tickDuration}
+     * @throws NullPointerException     if {@code unit} is {@code null}
+     * @throws IllegalArgumentException if {@code tickDuration} is &lt;= 0
+     */
+    public HashedWheelTimer(long tickDuration, TimeUnit unit) {
+        this(Executors.defaultThreadFactory(), tickDuration, unit);
+    }
+
+    /**
+     * Creates a new timer with the default thread factory
+     * ({@link Executors#defaultThreadFactory()}).
+     *
+     * @param tickDuration  the duration between tick
+     * @param unit          the time unit of the {@code tickDuration}
+     * @param ticksPerWheel the size of the wheel
+     * @throws NullPointerException     if {@code unit} is {@code null}
+     * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is &lt;= 0
+     */
+    public HashedWheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) {
+        this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel);
+    }
+
+    /**
+     * Creates a new timer with the default tick duration and default number of
+     * ticks per wheel.
+     *
+     * @param threadFactory a {@link ThreadFactory} that creates a
+     *                      background {@link Thread} which is dedicated to
+     *                      {@link TimerTask} execution.
+     * @throws NullPointerException if {@code threadFactory} is {@code null}
+     */
+    public HashedWheelTimer(ThreadFactory threadFactory) {
+        this(threadFactory, 100, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Creates a new timer with the default number of ticks per wheel.
+     *
+     * @param threadFactory a {@link ThreadFactory} that creates a
+     *                      background {@link Thread} which is dedicated to
+     *                      {@link TimerTask} execution.
+     * @param tickDuration  the duration between tick
+     * @param unit          the time unit of the {@code tickDuration}
+     * @throws NullPointerException     if either of {@code threadFactory} and {@code unit} is {@code null}
+     * @throws IllegalArgumentException if {@code tickDuration} is &lt;= 0
+     */
+    public HashedWheelTimer(
+            ThreadFactory threadFactory, long tickDuration, TimeUnit unit) {
+        this(threadFactory, tickDuration, unit, 512);
+    }
+
+    /**
+     * Creates a new timer.
+     *
+     * @param threadFactory a {@link ThreadFactory} that creates a
+     *                      background {@link Thread} which is dedicated to
+     *                      {@link TimerTask} execution.
+     * @param tickDuration  the duration between tick
+     * @param unit          the time unit of the {@code tickDuration}
+     * @param ticksPerWheel the size of the wheel
+     * @throws NullPointerException     if either of {@code threadFactory} and {@code unit} is {@code null}
+     * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is &lt;= 0
+     */
+    public HashedWheelTimer(
+            ThreadFactory threadFactory,
+            long tickDuration, TimeUnit unit, int ticksPerWheel) {
+        this(threadFactory, tickDuration, unit, ticksPerWheel, true);
+    }
+
+    /**
+     * Creates a new timer.
+     *
+     * @param threadFactory a {@link ThreadFactory} that creates a
+     *                      background {@link Thread} which is dedicated to
+     *                      {@link TimerTask} execution.
+     * @param tickDuration  the duration between tick
+     * @param unit          the time unit of the {@code tickDuration}
+     * @param ticksPerWheel the size of the wheel
+     * @param leakDetection {@code true} if leak detection should be enabled always,
+     *                      if false it will only be enabled if the worker thread is not
+     *                      a daemon thread.
+     * @throws NullPointerException     if either of {@code threadFactory} and {@code unit} is {@code null}
+     * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is &lt;= 0
+     */
+    public HashedWheelTimer(
+            ThreadFactory threadFactory,
+            long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection) {
+        this(threadFactory, tickDuration, unit, ticksPerWheel, leakDetection, -1);
+    }
+
+    /**
+     * Creates a new timer.
+     *
+     * @param threadFactory      a {@link ThreadFactory} that creates a
+     *                           background {@link Thread} which is dedicated to
+     *                           {@link TimerTask} execution.
+     * @param tickDuration       the duration between tick
+     * @param unit               the time unit of the {@code tickDuration}
+     * @param ticksPerWheel      the size of the wheel
+     * @param leakDetection      {@code true} if leak detection should be enabled always,
+     *                           if false it will only be enabled if the worker thread is not
+     *                           a daemon thread.
+     * @param maxPendingTimeouts The maximum number of pending timeouts after which call to
+     *                           {@code newTimeout} will result in
+     *                           {@link java.util.concurrent.RejectedExecutionException}
+     *                           being thrown. No maximum pending timeouts limit is assumed if
+     *                           this value is 0 or negative.
+     * @throws NullPointerException     if either of {@code threadFactory} and {@code unit} is {@code null}
+     * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is &lt;= 0
+     */
+    public HashedWheelTimer(
+            ThreadFactory threadFactory,
+            long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,
+            long maxPendingTimeouts) {
+
+        if (threadFactory == null) {
+            throw new NullPointerException("threadFactory");
+        }
+        if (unit == null) {
+            throw new NullPointerException("unit");
+        }
+        if (tickDuration <= 0) {
+            throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);
+        }
+        if (ticksPerWheel <= 0) {
+            throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);
+        }
+
+        // Normalize ticksPerWheel to power of two and initialize the wheel.
+        wheel = createWheel(ticksPerWheel);
+        mask = wheel.length - 1;
+
+        // Convert tickDuration to nanos.
+        long duration = unit.toNanos(tickDuration);
+
+        // Prevent overflow.
+        if (duration >= Long.MAX_VALUE / wheel.length) {
+            throw new IllegalArgumentException(String.format(
+                    "tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
+                    tickDuration, Long.MAX_VALUE / wheel.length));
+        }
+
+        if (duration < MILLISECOND_NANOS) {
+            logger.warn("Configured tickDuration {} smaller then {}, using 1ms.",
+                    tickDuration, MILLISECOND_NANOS);
+            this.tickDuration = MILLISECOND_NANOS;
+        } else {
+            this.tickDuration = duration;
+        }
+
+        workerThread = threadFactory.newThread(worker);
+
+        this.maxPendingTimeouts = maxPendingTimeouts;
+
+        if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&
+                WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {
+            reportTooManyInstances();
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            super.finalize();
+        } finally {
+            // This object is going to be GCed and it is assumed the ship has sailed to do a proper shutdown. If
+            // we have not yet shutdown then we want to make sure we decrement the active instance count.
+            if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) {
+                INSTANCE_COUNTER.decrementAndGet();
+            }
+        }
+    }
+
+    private static HashedWheelBucket[] createWheel(int ticksPerWheel) {
+        if (ticksPerWheel <= 0) {
+            throw new IllegalArgumentException(
+                    "ticksPerWheel must be greater than 0: " + ticksPerWheel);
+        }
+        if (ticksPerWheel > 1073741824) {
+            throw new IllegalArgumentException(
+                    "ticksPerWheel may not be greater than 2^30: " + ticksPerWheel);
+        }
+
+        ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);
+        HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
+        for (int i = 0; i < wheel.length; i++) {
+            wheel[i] = new HashedWheelBucket();
+        }
+        return wheel;
+    }
+
+    private static int normalizeTicksPerWheel(int ticksPerWheel) {
+        int normalizedTicksPerWheel = 1;
+        while (normalizedTicksPerWheel < ticksPerWheel) {
+            normalizedTicksPerWheel <<= 1;
+        }
+        return normalizedTicksPerWheel;
+    }
+
+    /**
+     * Starts the background thread explicitly.  The background thread will
+     * start automatically on demand even if you did not call this method.
+     *
+     * @throws IllegalStateException if this timer has been
+     *                               {@linkplain #stop() stopped} already
+     */
+    public void start() {
+        switch (WORKER_STATE_UPDATER.get(this)) {
+            case WORKER_STATE_INIT:
+                if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
+                    workerThread.start();
+                }
+                break;
+            case WORKER_STATE_STARTED:
+                break;
+            case WORKER_STATE_SHUTDOWN:
+                throw new IllegalStateException("cannot be started once stopped");
+            default:
+                throw new Error("Invalid WorkerState");
+        }
+
+        // Wait until the startTime is initialized by the worker.
+        while (startTime == 0) {
+            try {
+                startTimeInitialized.await();
+            } catch (InterruptedException ignore) {
+                // Ignore - it will be ready very soon.
+            }
+        }
+    }
+
+    @Override
+    public Set<Timeout> stop() {
+        if (Thread.currentThread() == workerThread) {
+            throw new IllegalStateException(
+                    HashedWheelTimer.class.getSimpleName() +
+                            ".stop() cannot be called from " +
+                            TimerTask.class.getSimpleName());
+        }
+
+        if (!WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_STARTED, WORKER_STATE_SHUTDOWN)) {
+            // workerState can be 0 or 2 at this moment - let it always be 2.
+            if (WORKER_STATE_UPDATER.getAndSet(this, WORKER_STATE_SHUTDOWN) != WORKER_STATE_SHUTDOWN) {
+                INSTANCE_COUNTER.decrementAndGet();
+            }
+
+            return Collections.emptySet();
+        }
+
+        try {
+            boolean interrupted = false;
+            while (workerThread.isAlive()) {
+                workerThread.interrupt();
+                try {
+                    workerThread.join(100);
+                } catch (InterruptedException ignored) {
+                    interrupted = true;
+                }
+            }
+
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        } finally {
+            INSTANCE_COUNTER.decrementAndGet();
+        }
+        return worker.unprocessedTimeouts();
+    }
+
+    @Override
+    public boolean isStop() {
+        return WORKER_STATE_SHUTDOWN == WORKER_STATE_UPDATER.get(this);
+    }
+
+    @Override
+    public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
+        if (task == null) {
+            throw new NullPointerException("task");
+        }
+        if (unit == null) {
+            throw new NullPointerException("unit");
+        }
+
+        long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();
+
+        if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {
+            pendingTimeouts.decrementAndGet();
+            throw new RejectedExecutionException("Number of pending timeouts ("
+                    + pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "
+                    + "timeouts (" + maxPendingTimeouts + ")");
+        }
+
+        start();
+
+        // Add the timeout to the timeout queue which will be processed on the next tick.
+        // During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.
+        long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
+
+        // Guard against overflow.
+        if (delay > 0 && deadline < 0) {
+            deadline = Long.MAX_VALUE;
+        }
+        HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
+        timeouts.add(timeout);
+        return timeout;
+    }
+
+    /**
+     * Returns the number of pending timeouts of this {@link Timer}.
+     */
+    public long pendingTimeouts() {
+        return pendingTimeouts.get();
+    }
+
+    private static void reportTooManyInstances() {
+        if (logger.isErrorEnabled()) {
+            String resourceType = HashedWheelTimer.class.getSimpleName();
+            logger.error("You are creating too many " + resourceType + " instances. " +
+                    resourceType + " is a shared resource that must be reused across the JVM," +
+                    "so that only a few instances are created.");
+        }
+    }
+
+    private final class Worker implements Runnable {
+        private final Set<Timeout> unprocessedTimeouts = new HashSet<Timeout>();
+
+        private long tick;
+
+        @Override
+        public void run() {
+            // Initialize the startTime.
+            startTime = System.nanoTime();
+            if (startTime == 0) {
+                // We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized.
+                startTime = 1;
+            }
+
+            // Notify the other threads waiting for the initialization at start().
+            startTimeInitialized.countDown();
+
+            do {
+                final long deadline = waitForNextTick();
+                if (deadline > 0) {
+                    int idx = (int) (tick & mask);
+                    processCancelledTasks();
+                    HashedWheelBucket bucket =
+                            wheel[idx];
+                    transferTimeoutsToBuckets();
+                    bucket.expireTimeouts(deadline);
+                    tick++;
+                }
+            } while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
+
+            // Fill the unprocessedTimeouts so we can return them from stop() method.
+            for (HashedWheelBucket bucket : wheel) {
+                bucket.clearTimeouts(unprocessedTimeouts);
+            }
+            for (; ; ) {
+                HashedWheelTimeout timeout = timeouts.poll();
+                if (timeout == null) {
+                    break;
+                }
+                if (!timeout.isCancelled()) {
+                    unprocessedTimeouts.add(timeout);
+                }
+            }
+            processCancelledTasks();
+        }
+
+        private void transferTimeoutsToBuckets() {
+            // transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just
+            // adds new timeouts in a loop.
+            for (int i = 0; i < 100000; i++) {
+                HashedWheelTimeout timeout = timeouts.poll();
+                if (timeout == null) {
+                    // all processed
+                    break;
+                }
+                if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) {
+                    // Was cancelled in the meantime.
+                    continue;
+                }
+
+                long calculated = timeout.deadline / tickDuration;
+                timeout.remainingRounds = (calculated - tick) / wheel.length;
+
+                final long ticks = Math.max(calculated, tick); // Ensure we don't schedule for past.
+                int stopIndex = (int) (ticks & mask);
+
+                HashedWheelBucket bucket = wheel[stopIndex];
+                bucket.addTimeout(timeout);
+            }
+        }
+
+        private void processCancelledTasks() {
+            for (; ; ) {
+                HashedWheelTimeout timeout = cancelledTimeouts.poll();
+                if (timeout == null) {
+                    // all processed
+                    break;
+                }
+                try {
+                    timeout.remove();
+                } catch (Throwable t) {
+                    if (logger.isWarnEnabled()) {
+                        logger.warn("An exception was thrown while process a cancellation task", t);
+                    }
+                }
+            }
+        }
+
+        /**
+         * calculate goal nanoTime from startTime and current tick number,
+         * then wait until that goal has been reached.
+         *
+         * @return Long.MIN_VALUE if received a shutdown request,
+         * current time otherwise (with Long.MIN_VALUE changed by +1)
+         */
+        private long waitForNextTick() {
+            long deadline = tickDuration * (tick + 1);
+
+            for (; ; ) {
+                final long currentTime = System.nanoTime() - startTime;
+                long sleepTimeMs = (deadline - currentTime + 999999) / 1000000;
+
+                if (sleepTimeMs <= 0) {
+                    if (currentTime == Long.MIN_VALUE) {
+                        return -Long.MAX_VALUE;
+                    } else {
+                        return currentTime;
+                    }
+                }
+
+                // Check if we run on windows, as if thats the case we will need
+                // to round the sleepTime as workaround for a bug that only affect
+                // the JVM if it runs on windows.
+                //
+                // See https://github.com/netty/netty/issues/356
+                if (OsUtils.isWindows()) {
+                    sleepTimeMs = sleepTimeMs / 10 * 10;
+                }
+
+                try {
+                    Thread.sleep(sleepTimeMs);
+                } catch (InterruptedException ignored) {
+                    if (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_SHUTDOWN) {
+                        return Long.MIN_VALUE;
+                    }
+                }
+            }
+        }
+
+        public Set<Timeout> unprocessedTimeouts() {
+            return Collections.unmodifiableSet(unprocessedTimeouts);
+        }
+    }
+
+    private static final class HashedWheelTimeout implements Timeout {
+
+        private static final int ST_INIT = 0;
+        private static final int ST_CANCELLED = 1;
+        private static final int ST_EXPIRED = 2;
+        private static final AtomicIntegerFieldUpdater<HashedWheelTimeout> STATE_UPDATER =
+                AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimeout.class, "state");
+
+        private final HashedWheelTimer timer;
+        private final TimerTask task;
+        private final long deadline;
+
+        @SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization"})
+        private volatile int state = ST_INIT;
+
+        // remainingRounds will be calculated and set by Worker.transferTimeoutsToBuckets() before the
+        // HashedWheelTimeout will be added to the correct HashedWheelBucket.
+        long remainingRounds;
+
+        // This will be used to chain timeouts in HashedWheelTimerBucket via a double-linked-list.
+        // As only the workerThread will act on it there is no need for synchronization / volatile.
+        HashedWheelTimeout next;
+        HashedWheelTimeout prev;
+
+        // The bucket to which the timeout was added
+        HashedWheelBucket bucket;
+
+        HashedWheelTimeout(HashedWheelTimer timer, TimerTask task, long deadline) {
+            this.timer = timer;
+            this.task = task;
+            this.deadline = deadline;
+        }
+
+        @Override
+        public Timer timer() {
+            return timer;
+        }
+
+        @Override
+        public TimerTask task() {
+            return task;
+        }
+
+        @Override
+        public boolean cancel() {
+            // only update the state it will be removed from HashedWheelBucket on next tick.
+            if (!compareAndSetState(ST_INIT, ST_CANCELLED)) {
+                return false;
+            }
+            // If a task should be canceled we put this to another queue which will be processed on each tick.
+            // So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way
+            // we can make again use of our MpscLinkedQueue and so minimize the locking / overhead as much as possible.
+            timer.cancelledTimeouts.add(this);
+            return true;
+        }
+
+        void remove() {
+            HashedWheelBucket bucket = this.bucket;
+            if (bucket != null) {
+                bucket.remove(this);
+            } else {
+                timer.pendingTimeouts.decrementAndGet();
+            }
+        }
+
+        public boolean compareAndSetState(int expected, int state) {
+            return STATE_UPDATER.compareAndSet(this, expected, state);
+        }
+
+        public int state() {
+            return state;
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return state() == ST_CANCELLED;
+        }
+
+        @Override
+        public boolean isExpired() {
+            return state() == ST_EXPIRED;
+        }
+
+        public void expire() {
+            if (!compareAndSetState(ST_INIT, ST_EXPIRED)) {
+                return;
+            }
+
+            try {
+                task.run(this);
+            } catch (Throwable t) {
+                if (logger.isWarnEnabled()) {
+                    logger.warn("An exception was thrown by " + TimerTask.class.getSimpleName() + '.', t);
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            final long currentTime = System.nanoTime();
+            long remaining = deadline - currentTime + timer.startTime;
+
+            StringBuilder buf = new StringBuilder(192)
+                    .append(this.getClass().getSimpleName())
+                    .append('(')
+                    .append("deadline: ");
+            if (remaining > 0) {
+                buf.append(remaining)
+                        .append(" ns later");
+            } else if (remaining < 0) {
+                buf.append(-remaining)
+                        .append(" ns ago");
+            } else {
+                buf.append("now");
+            }
+
+            if (isCancelled()) {
+                buf.append(", cancelled");
+            }
+
+            return buf.append(", task: ")
+                    .append(task())
+                    .append(')')
+                    .toString();
+        }
+
+    }
+
+    /**
+     * Bucket that stores HashedWheelTimeouts. These are stored in a linked-list like datastructure to allow easy
+     * removal of HashedWheelTimeouts in the middle. Also the HashedWheelTimeout act as nodes themself and so no
+     * extra object creation is needed.
+     */
+    private static final class HashedWheelBucket {
+        // Used for the linked-list datastructure
+        private HashedWheelTimeout head;
+        private HashedWheelTimeout tail;
+
+        /**
+         * Add {@link HashedWheelTimeout} to this bucket.
+         */
+        public void addTimeout(HashedWheelTimeout timeout) {
+            assert timeout.bucket == null;
+            timeout.bucket = this;
+            if (head == null) {
+                head = tail = timeout;
+            } else {
+                tail.next = timeout;
+                timeout.prev = tail;
+                tail = timeout;
+            }
+        }
+
+        /**
+         * Expire all {@link HashedWheelTimeout}s for the given {@code deadline}.
+         */
+        public void expireTimeouts(long deadline) {
+            HashedWheelTimeout timeout = head;
+
+            // process all timeouts
+            while (timeout != null) {
+                HashedWheelTimeout next = timeout.next;
+                if (timeout.remainingRounds <= 0) {
+                    next = remove(timeout);
+                    if (timeout.deadline <= deadline) {
+                        timeout.expire();
+                    } else {
+                        // The timeout was placed into a wrong slot. This should never happen.
+                        throw new IllegalStateException(String.format(
+                                "timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline));
+                    }
+                } else if (timeout.isCancelled()) {
+                    next = remove(timeout);
+                } else {
+                    timeout.remainingRounds--;
+                }
+                timeout = next;
+            }
+        }
+
+        public HashedWheelTimeout remove(HashedWheelTimeout timeout) {
+            HashedWheelTimeout next = timeout.next;
+            // remove timeout that was either processed or cancelled by updating the linked-list
+            if (timeout.prev != null) {
+                timeout.prev.next = next;
+            }
+            if (timeout.next != null) {
+                timeout.next.prev = timeout.prev;
+            }
+
+            if (timeout == head) {
+                // if timeout is also the tail we need to adjust the entry too
+                if (timeout == tail) {
+                    tail = null;
+                    head = null;
+                } else {
+                    head = next;
+                }
+            } else if (timeout == tail) {
+                // if the timeout is the tail modify the tail to be the prev node.
+                tail = timeout.prev;
+            }
+            // null out prev, next and bucket to allow for GC.
+            timeout.prev = null;
+            timeout.next = null;
+            timeout.bucket = null;
+            timeout.timer.pendingTimeouts.decrementAndGet();
+            return next;
+        }
+
+        /**
+         * Clear this bucket and return all not expired / cancelled {@link Timeout}s.
+         */
+        public void clearTimeouts(Set<Timeout> set) {
+            for (; ; ) {
+                HashedWheelTimeout timeout = pollTimeout();
+                if (timeout == null) {
+                    return;
+                }
+                if (timeout.isExpired() || timeout.isCancelled()) {
+                    continue;
+                }
+                set.add(timeout);
+            }
+        }
+
+        private HashedWheelTimeout pollTimeout() {
+            HashedWheelTimeout head = this.head;
+            if (head == null) {
+                return null;
+            }
+            HashedWheelTimeout next = head.next;
+            if (next == null) {
+                tail = this.head = null;
+            } else {
+                this.head = next;
+                next.prev = null;
+            }
+
+            // null out prev and next to allow for GC.
+            head.next = null;
+            head.prev = null;
+            head.bucket = null;
+            return head;
+        }
+    }
+}
diff --git a/soul-common/src/main/java/org/dromara/soul/common/timer/Timeout.java b/soul-common/src/main/java/org/dromara/soul/common/timer/Timeout.java
new file mode 100644
index 0000000..02ef6e4
--- /dev/null
+++ b/soul-common/src/main/java/org/dromara/soul/common/timer/Timeout.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.dromara.soul.common.timer;
+
+/**
+ * A handle associated with a {@link TimerTask} that is returned by a
+ * {@link Timer}.
+ *
+ * @author netty.
+ */
+public interface Timeout {
+
+    /**
+     * Returns the {@link Timer} that created this handle.
+     */
+    Timer timer();
+
+    /**
+     * Returns the {@link TimerTask} which is associated with this handle.
+     */
+    TimerTask task();
+
+    /**
+     * Returns {@code true} if and only if the {@link TimerTask} associated
+     * with this handle has been expired.
+     */
+    boolean isExpired();
+
+    /**
+     * Returns {@code true} if and only if the {@link TimerTask} associated
+     * with this handle has been cancelled.
+     */
+    boolean isCancelled();
+
+    /**
+     * Attempts to cancel the {@link TimerTask} associated with this handle.
+     * If the task has been executed or cancelled already, it will return with
+     * no side effect.
+     *
+     * @return True if the cancellation completed successfully, otherwise false
+     */
+    boolean cancel();
+}
\ No newline at end of file
diff --git a/soul-common/src/main/java/org/dromara/soul/common/timer/Timer.java b/soul-common/src/main/java/org/dromara/soul/common/timer/Timer.java
new file mode 100644
index 0000000..a26cc6a
--- /dev/null
+++ b/soul-common/src/main/java/org/dromara/soul/common/timer/Timer.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2012 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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 org.dromara.soul.common.timer;
+
+import java.util.Set;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Schedules {@link TimerTask}s for one-time future execution in a background
+ * thread.
+ *
+ * @author netty.
+ */
+public interface Timer {
+
+    /**
+     * Schedules the specified {@link TimerTask} for one-time execution after
+     * the specified delay.
+     *
+     * @return a handle which is associated with the specified task
+     * @throws IllegalStateException      if this timer has been {@linkplain #stop() stopped} already
+     * @throws RejectedExecutionException if the pending timeouts are too many and creating new timeout
+     *                                    can cause instability in the system.
+     */
+    Timeout newTimeout(TimerTask task, long delay, TimeUnit unit);
+
+    /**
+     * Releases all resources acquired by this {@link Timer} and cancels all
+     * tasks which were scheduled but not executed yet.
+     *
+     * @return the handles associated with the tasks which were canceled by
+     * this method
+     */
+    Set<Timeout> stop();
+
+    /**
+     *  stop method status.
+     * @return bool.
+     */
+    boolean isStop();
+}
diff --git a/soul-common/src/main/java/org/dromara/soul/common/timer/TimerTask.java b/soul-common/src/main/java/org/dromara/soul/common/timer/TimerTask.java
new file mode 100644
index 0000000..c26526a
--- /dev/null
+++ b/soul-common/src/main/java/org/dromara/soul/common/timer/TimerTask.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.dromara.soul.common.timer;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A task which is executed after the delay specified with
+ * {@link Timer#newTimeout(TimerTask, long, TimeUnit)}.
+ * io.netty.util.TimerTask.
+ *
+ * @author netty.
+ */
+public interface TimerTask {
+
+    /**
+     * Executed after the delay specified with
+     * {@link Timer#newTimeout(TimerTask, long, TimeUnit)}.
+     *
+     * @param timeout a handle which is associated with this task
+     */
+    void run(Timeout timeout);
+}
diff --git a/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/AbstractRegistry.java b/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/AbstractRegistry.java
index 09e3aef..57d2d3f 100644
--- a/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/AbstractRegistry.java
+++ b/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/AbstractRegistry.java
@@ -17,11 +17,16 @@
 
 package org.dromara.soul.register.api;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+
+import com.google.common.collect.Sets;
 import org.dromara.soul.common.exception.SoulException;
 import org.dromara.soul.common.http.URL;
+import org.dromara.soul.register.api.exception.RegisterException;
 
 /**
  * AbstractRegistry.
@@ -41,12 +46,12 @@
     /**
      * The notifyUrls.
      */
-    protected final Map<URL, RegisterNotifyListener> subscribed = new ConcurrentHashMap<>();
+    private final Map<URL, RegisterNotifyListener> subscribed = new ConcurrentHashMap<>();
 
     /**
      * The Registered.
      */
-    protected final Map<URL, Object> registered = new ConcurrentHashMap<>();
+    private final Set<URL> registered = Sets.newConcurrentHashSet();
 
     /**
      * Instantiates a new Abstract registry.
@@ -69,10 +74,21 @@
     }
 
     /**
-     * Retry.
+     * get subscribed.
+     *
+     * @return map.
      */
-    public void retry() {
-        //todo:重试.
+    Map<URL, RegisterNotifyListener> getSubscribed() {
+        return Collections.unmodifiableMap(subscribed);
+    }
+
+    /**
+     * get registered.
+     *
+     * @return set.
+     */
+    Set<URL> getRegistered() {
+        return Collections.unmodifiableSet(registered);
     }
 
     /**
@@ -89,7 +105,7 @@
      *
      * @return the remote url
      */
-    public URL getRemoteUrl() {
+    protected URL getRemoteUrl() {
         return remoteUrl;
     }
 
@@ -103,22 +119,38 @@
 
     @Override
     public void register(URL url) {
-
+        if (url == null) {
+            throw new RegisterException("url is null || url == null");
+        }
+        registered.add(url);
     }
 
+
     @Override
     public void unregister(URL url) {
-
+        if (url == null) {
+            throw new RegisterException("url is null || url == null");
+        }
+        registered.remove(url);
     }
 
     @Override
     public void subscribe(URL url, RegisterNotifyListener listener) {
-
+        if (url == null) {
+            throw new RegisterException("url is null || url == null");
+        }
+        if (listener == null) {
+            throw new RegisterException("listener is null || listener == null");
+        }
+        subscribed.putIfAbsent(url, listener);
     }
 
     @Override
     public void unsubscribe(URL url) {
-
+        if (url == null) {
+            throw new RegisterException("url is null || url == null");
+        }
+        subscribed.remove(url);
     }
 
     @Override
diff --git a/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/FailbackRegistry.java b/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/FailbackRegistry.java
new file mode 100644
index 0000000..d861987
--- /dev/null
+++ b/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/FailbackRegistry.java
@@ -0,0 +1,280 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.dromara.soul.register.api;
+
+import com.google.common.collect.Maps;
+import org.dromara.soul.common.concurrent.SoulThreadFactory;
+import org.dromara.soul.common.http.URL;
+import org.dromara.soul.common.timer.HashedWheelTimer;
+import org.dromara.soul.common.timer.Timeout;
+import org.dromara.soul.common.timer.Timer;
+import org.dromara.soul.common.timer.TimerTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * FailbackRegistry .
+ *
+ * @author sixh
+ */
+public abstract class FailbackRegistry extends AbstractRegistry {
+
+    private Logger logger = LoggerFactory.getLogger(FailbackRegistry.class);
+
+    private final Map<URL, FailbackTask> failbackRegister = Maps.newConcurrentMap();
+
+    private final Map<URL, FailbackTask> failbackSubscribe = Maps.newConcurrentMap();
+
+    private static final HashedWheelTimer TIMER = new HashedWheelTimer(SoulThreadFactory.create("soul.failback.register"));
+    /**
+     * retry exec time.
+     */
+    private final Long retryTime = TimeUnit.SECONDS.toMillis(5);
+
+    /**
+     * Instantiates a new Abstract registry.
+     *
+     * @param url the url.
+     */
+    public FailbackRegistry(URL url) {
+        super(url);
+    }
+
+    @Override
+    public void register(URL url) {
+        removeFailbackRegister(url);
+        try {
+            super.register(url);
+            doRegister(url);
+        } catch (Throwable t) {
+            addFailbackRegister(url, new FailbackTask(url, url1 -> {
+                doRegister(url1);
+                failbackRegister.remove(url1);
+            }, retryTime));
+        }
+    }
+
+    @Override
+    public void unregister(URL url) {
+        removeFailbackRegister(url);
+        try {
+            super.unregister(url);
+            doUnRegister(url);
+        } catch (Throwable t) {
+            addFailbackRegister(url, new FailbackTask(url, url1 -> {
+                doUnRegister(url1);
+                failbackRegister.remove(url1);
+            }, retryTime));
+        }
+    }
+
+    @Override
+    public void subscribe(URL url, RegisterNotifyListener listener) {
+        removeFailbackSubscribe(url);
+        try {
+            super.subscribe(url, listener);
+            doSubscribe(url);
+        } catch (Throwable t) {
+            addFailbackSubscribe(url, new FailbackTask(url, url1 -> {
+                doSubscribe(url1);
+                failbackSubscribe.remove(url1);
+            }, retryTime));
+        }
+    }
+
+    @Override
+    public void unsubscribe(URL url) {
+        removeFailbackSubscribe(url);
+        try {
+            super.unsubscribe(url);
+            doUnSubscribe(url);
+        } catch (Throwable t) {
+            addFailbackSubscribe(url, new FailbackTask(url, url1 -> {
+                doUnSubscribe(url1);
+                failbackSubscribe.remove(url1);
+            }, retryTime));
+        }
+    }
+
+    private void addFailbackRegister(URL url, FailbackTask task) {
+        FailbackTask failbackTask = failbackRegister.get(url);
+        if (failbackTask != null) {
+            return;
+        }
+        failbackRegister.putIfAbsent(url, task);
+        TIMER.newTimeout(task, retryTime, TimeUnit.MILLISECONDS);
+    }
+
+    private void removeFailbackRegister(URL url) {
+        FailbackTask remove = failbackRegister.remove(url);
+        if (remove != null) {
+            remove.cancel();
+        }
+    }
+
+    private void addFailbackSubscribe(URL url, FailbackTask task) {
+        FailbackTask failbackTask = failbackRegister.get(url);
+        if (failbackTask != null) {
+            return;
+        }
+        failbackSubscribe.putIfAbsent(url, task);
+        TIMER.newTimeout(task, retryTime, TimeUnit.MILLISECONDS);
+    }
+
+    private void removeFailbackSubscribe(URL url) {
+        FailbackTask remove = failbackSubscribe.remove(url);
+        if (remove != null) {
+            remove.cancel();
+        }
+    }
+
+    protected void recover() {
+        Set<URL> registered = getRegistered();
+        if (!registered.isEmpty()) {
+            if (logger.isInfoEnabled()) {
+                logger.info("registered : {}", registered);
+            }
+            registered.forEach(e -> addFailbackSubscribe(e, new FailbackTask(e,
+                    url -> {
+                        doRegister(url);
+                        failbackRegister.remove(url);
+                    }, retryTime)));
+        }
+        Map<URL, RegisterNotifyListener> subscribed = getSubscribed();
+        if (!subscribed.isEmpty()) {
+            if (logger.isInfoEnabled()) {
+                logger.info("subscribed: {}", subscribed);
+            }
+            subscribed.forEach((k, v) -> addFailbackSubscribe(k, new FailbackTask(k, url -> {
+                doSubscribe(url);
+                failbackSubscribe.remove(url);
+            }, retryTime)));
+        }
+    }
+
+    /**
+     * register template method.
+     *
+     * @param url url.
+     */
+    protected abstract void doRegister(URL url);
+
+    /**
+     * Do un register.
+     *
+     * @param url the url
+     */
+    protected abstract void doUnRegister(URL url);
+
+    /**
+     * Do subscribe.
+     *
+     * @param url the url
+     */
+    protected abstract void doSubscribe(URL url);
+
+    /**
+     * Do un subscribe.
+     *
+     * @param url the url
+     */
+    protected abstract void doUnSubscribe(URL url);
+
+    /**
+     * The type Failback task.
+     */
+    static class FailbackTask implements TimerTask {
+
+        private FailbackRunner runner;
+
+        private URL url;
+
+        private Long tick;
+
+        private volatile boolean cancel = false;
+
+        /**
+         * Instantiates a new Failback task.
+         *
+         * @param url    the url
+         * @param runner the runner
+         * @param tick   the tick
+         */
+        FailbackTask(URL url, FailbackRunner runner, Long tick) {
+            this.runner = runner;
+            this.url = url;
+            this.tick = tick;
+        }
+
+        /**
+         * Cancel.
+         */
+        void cancel() {
+            this.cancel = true;
+        }
+
+        /**
+         * Is cancel boolean.
+         *
+         * @return the boolean
+         */
+        private boolean isCancel() {
+            return cancel;
+        }
+
+        @Override
+        public void run(Timeout timeout) {
+            if (timeout.isCancelled() || timeout.timer().isStop() || isCancel()) {
+                // other thread cancel this timeout or stop the timer.
+                return;
+            }
+            try {
+                runner.apply(url);
+            } catch (Throwable e) {
+                replay(timeout, tick);
+            }
+        }
+
+        private void replay(Timeout timeout, long tick) {
+            Timer timer = timeout.timer();
+            if (timer.isStop() || timeout.isCancelled() || isCancel()) {
+                return;
+            }
+            timer.newTimeout(this, tick, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    /**
+     * The interface Failback runner.
+     */
+    @FunctionalInterface
+    interface FailbackRunner {
+        /**
+         * Apply.
+         *
+         * @param url the url
+         */
+        void apply(URL url);
+    }
+}
diff --git a/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/exception/RegisterException.java b/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/exception/RegisterException.java
new file mode 100644
index 0000000..d6e1fe2
--- /dev/null
+++ b/soul-register/soul-register-api/src/main/java/org/dromara/soul/register/api/exception/RegisterException.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.dromara.soul.register.api.exception;
+
+import org.dromara.soul.common.exception.SoulException;
+
+/**
+ * RegisterException .
+ *
+ * @author sixh
+ */
+public class RegisterException extends SoulException {
+    public RegisterException(Throwable e) {
+        super(e);
+    }
+
+    public RegisterException(String message) {
+        super(message);
+    }
+
+    public RegisterException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/soul-register/soul-register-zookeeper/src/main/java/ZookeeperRegistry.java b/soul-register/soul-register-zookeeper/src/main/java/ZookeeperRegistry.java
index 7f55846..f977a59 100644
--- a/soul-register/soul-register-zookeeper/src/main/java/ZookeeperRegistry.java
+++ b/soul-register/soul-register-zookeeper/src/main/java/ZookeeperRegistry.java
@@ -15,16 +15,17 @@
  *     limitations under the License.
  */
 
-import java.util.Optional;
 import org.dromara.soul.common.exception.SoulException;
 import org.dromara.soul.common.extension.Join;
 import org.dromara.soul.common.http.URL;
-import org.dromara.soul.register.api.AbstractRegistry;
+import org.dromara.soul.register.api.FailbackRegistry;
 import org.dromara.soul.register.api.RegisterConst;
 import org.dromara.soul.remoting.zookeeper.ZookeeperClient;
 import org.dromara.soul.remoting.zookeeper.ZookeeperClientCache;
 import org.dromara.soul.remoting.zookeeper.ZookeeperStatusCallback;
 
+import java.util.Optional;
+
 import static org.dromara.soul.register.api.RegisterConst.BASE_URL_PATH_KEY;
 
 /**
@@ -34,7 +35,7 @@
  * @author sixh
  */
 @Join
-public class ZookeeperRegistry extends AbstractRegistry {
+public class ZookeeperRegistry extends FailbackRegistry {
 
     private final ZookeeperClient client;
 
@@ -43,7 +44,7 @@
         client = ZookeeperClientCache.getClient(url);
         client.statusChange(connectionState -> {
             if (connectionState.equals(ZookeeperStatusCallback.RECONNECTED)) {
-                retry();
+                recover();
             }
         });
     }
@@ -64,15 +65,6 @@
         return path + url.encode();
     }
 
-    @Override
-    public void register(URL url) {
-        try {
-            String path = toPath(url);
-            client.create(path, isEphemeral(url));
-        } catch (Throwable e) {
-            throw new SoulException("register failed " + url + "to zookeeper " + getRemoteUrl());
-        }
-    }
 
     private boolean isEphemeral(URL url) {
         String parameter = url.getParameter(RegisterConst.EPHEMERAL_KEY);
@@ -82,11 +74,31 @@
     }
 
     @Override
-    public void unregister(URL url) {
+    protected void doRegister(URL url) {
+        try {
+            String path = toPath(url);
+            client.create(path, isEphemeral(url));
+        } catch (Throwable e) {
+            throw new SoulException("register failed " + url + "to zookeeper " + getRemoteUrl());
+        }
+    }
+
+    @Override
+    protected void doUnRegister(URL url) {
         try {
             client.delete(toPath(url));
         } catch (Throwable e) {
             throw new SoulException("unregister failed " + url + "to zookeeper " + getRemoteUrl());
         }
     }
+
+    @Override
+    protected void doSubscribe(URL url) {
+
+    }
+
+    @Override
+    protected void doUnSubscribe(URL url) {
+
+    }
 }
diff --git a/soul-remoting/soul-remoting-api/src/main/java/org/dromara/soul/remoting/api/ChannelCache.java b/soul-remoting/soul-remoting-api/src/main/java/org/dromara/soul/remoting/api/ChannelCache.java
index 138a9800f..8434ae3 100644
--- a/soul-remoting/soul-remoting-api/src/main/java/org/dromara/soul/remoting/api/ChannelCache.java
+++ b/soul-remoting/soul-remoting-api/src/main/java/org/dromara/soul/remoting/api/ChannelCache.java
@@ -107,10 +107,10 @@
     Channel get(String key) {
         Timeout timeout = CACHES.get(key);
         if (timeout != null && !timeout.isDefault()) {
-            Node node = (Node) timeout.task();
-            String sKey = node.getKey();
+            TtlCacheTimerTask ttlCacheTimerTask = (TtlCacheTimerTask) timeout.task();
+            String sKey = ttlCacheTimerTask.getKey();
             if (sKey.equals(key)) {
-                return node.getValue();
+                return ttlCacheTimerTask.getValue();
             }
         }
         return null;
@@ -154,6 +154,6 @@
 
     @SuppressWarnings("unchecked")
     public Collection<Channel> getAll() {
-        return CACHES.values().stream().map(e -> ((Node) e.task()).getValue()).collect(Collectors.toList());
+        return CACHES.values().stream().map(e -> ((TtlCacheTimerTask) e.task()).getValue()).collect(Collectors.toList());
     }
 }