@Override
    protected void recover() throws Exception {
        // register
        Set<URL> recoverRegistered = new HashSet<>(getRegistered());
        if (!recoverRegistered.isEmpty()) {
            if (logger.isInfoEnabled()) {
                logger.info("Recover register url " + recoverRegistered);
            }
            for (URL url : recoverRegistered) {
                // remove fail registry or unRegistry task first.
                removeFailedRegistered(url);
                removeFailedUnregistered(url);
                addFailedRegistered(url);
            }
        }
        // subscribe
        Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<>(getSubscribed());
        if (!recoverSubscribed.isEmpty()) {
            if (logger.isInfoEnabled()) {
                logger.info("Recover subscribe url " + recoverSubscribed.keySet());
            }
            for (Map.Entry<URL, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {
                URL url = entry.getKey();
                for (NotifyListener listener : entry.getValue()) {
                    // First remove other tasks to ensure that addFailedSubscribed can succeed.
                    removeFailedSubscribed(url, listener);
                    addFailedSubscribed(url, listener);
                }
            }
        }
    }
@Test
    void testRecover() throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        final AtomicReference<Boolean> notified = new AtomicReference<Boolean>(false);
        NotifyListener listener = urls -> notified.set(Boolean.TRUE);

        MockRegistry mockRegistry = new MockRegistry(registryUrl, serviceUrl, countDownLatch);
        mockRegistry.register(serviceUrl);
        mockRegistry.subscribe(serviceUrl, listener);
        Assertions.assertEquals(1, mockRegistry.getRegistered().size());
        Assertions.assertEquals(1, mockRegistry.getSubscribed().size());
        mockRegistry.recover();
        countDownLatch.await();
        Assertions.assertEquals(0, mockRegistry.getFailedRegistered().size());
        FailbackRegistry.Holder h = new FailbackRegistry.Holder(registryUrl, listener);
        Assertions.assertNull(mockRegistry.getFailedSubscribed().get(h));
        Assertions.assertEquals(countDownLatch.getCount(), 0);
    }