Dubbo源码(3)-基础抽象注册中心源码解析

Dubbo注册中心扩展,我造了个这样的轮子来冲KPI hello大家好呀! 作为一名基础组件开发,服务好每一位业务开发同学是我们的义务(KPI)。 客服群里经常有业务开发同学丢来一段代码、一个报错,而我们,当然要微笑服务,耐心解答。 有的问题,凭借多年踩坑经验,一眼就能看出;有的问题,看一眼代码也能知道原因,但有的问题,还真就光凭看是看不出来的,这时,只能下载代码,本地跑跑看了。 熟悉我的朋友都知道,我从事dubbo相关开(客)发(服)工作多年,所以我就来讲一个dubbo问题排查过程中的有趣的事。 通常遇到看不能解决的问题时,先git拉取代码,. 阅读详情

本文主要参考自Dubbo官方文档、Dubbo项目源码以及网络文章和相关书籍,并附上自身的一些理解,如有遗漏或错误,还望海涵并指出。谢谢!

------本文基于Dubbo-2.6.1版本

一.Dubbo注册中心

Dubbo的注册中心的作用接受生产者的服务注册和客户端的订阅,也就是服务间的“中间人”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N4eKdwkW-1588683498445)(http://note.youdao.com/yws/res/20595/11AAF08395C14C4F9FE2D6BB8A62FD6A)]

整个Dubbo注册中心的实现位于dubbo-registry模块下:

在这里插入图片描述
从这张依赖图来看,注册中心存在着dubbo、zk、redis和multicast这四种实现,它们都依赖于dubbo-registry-api.com.alibaba.dubbo.registry.support这个包,所以本章主要是来看dubbo-registry-api的代码实现。
在这里插入图片描述

二.代码构成

  • dubbo-registry-api
    在这里插入图片描述

  • 依赖关系
    在这里插入图片描述

  • 代码量统计
    在这里插入图片描述

三、RegistryFactory

com.alibaba.dubbo.registry.RegistryFactory是注册中心接口工厂,采用SPI机制动态拓展:

  • RegistryFactory
@SPI("dubbo")
public interface RegistryFactory {

    /**
     * 连接注册中心.
     * <p>
     * 连接注册中心需处理契约:<br>
     * 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。<br>
     * 2. 支持URL上的username:password权限认证。<br>
     * 3. 支持backup=10.20.153.10备选注册中心集群地址。<br>
     * 4. 支持file=registry.cache本地磁盘文件缓存。<br>
     * 5. 支持timeout=1000请求超时设置。<br>
     * 6. 支持session=60000会话超时或过期设置。<br>
     *
     * @param url 注册中心地址,不允许为空
     * @return 注册中心引用,总不返回空
     */
    @Adaptive({"protocol"})
    Registry getRegistry(URL url);

}

@Adaptive({“protocol”}) 注解,Dubbo SPI 会自动实现 RegistryFactory$Adaptive 类,根据 url.protocol 获得对应的 RegistryFactory 实现类。例如,url.protocol = zookeeper 时,获得 ZookeeperRegistryFactory 实现类。

3.1、AbstractRegistryFactory

com.alibaba.dubbo.registry.support.AbstractRegistryFactory是RegistryFactory的抽象实现类,提供了对Registry(注册中心)的管理。

  • AbstractRegistryFactory
public abstract class AbstractRegistryFactory implements RegistryFactory {

    // logger
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRegistryFactory.class);

    // 获取注册中心过程中使用的互斥锁
    private static final ReentrantLock LOCK = new ReentrantLock();

    // 注册中心映射Map<key=注册名称、value=注册中心>
    private static final Map<String, Registry> REGISTRIES = new ConcurrentHashMap<String, Registry>();

    /**
     * 获取到所有注册中心
     *
     * @return all registries
     */
    public static Collection<Registry> getRegistries() {
        return Collections.unmodifiableCollection(REGISTRIES.values());
    }

    /**
     * 关闭所有注册中心
     */
    // TODO: 2017/8/30 to move somewhere else better
    public static void destroyAll() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Close all registries " + getRegistries());
        }
        // Lock up the registry shutdown process
        LOCK.lock();
        try {
            for (Registry registry : getRegistries()) {
                try {
                    // 调用关闭方法
                    registry.destroy();
                } catch (Throwable e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
            // 清空Map
            REGISTRIES.clear();
        } finally {
            // Release the lock
            LOCK.unlock();
        }
    }

    /*
    *   解析URL获取指定注册中心
     */
    public Registry getRegistry(URL url) {
        url = url.setPath(RegistryService.class.getName())
                .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
                .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
        String key = url.toServiceString();
        // Lock the registry access process to ensure a single instance of the registry
        LOCK.lock();
        try {
            Registry registry = REGISTRIES.get(key);
            if (registry != null) {
                return registry;
            }
            // registry==null,create
            registry = createRegistry(url);
            if (registry == null) {
                throw new IllegalStateException("Can not create registry " + url);
            }
            // 将创建的注册中心放到Map中
            REGISTRIES.put(key, registry);
            return registry;
        } finally {
            // Release the lock
            LOCK.unlock();
        }
    }
    /*
    *   模板方法模式,留给子类做实现
    *   创建注册中心
     */
    protected abstract Registry createRegistry(URL url);

}

四、RegistryService

com.alibaba.dubbo.registry.RegistryService,注册中心服务接口,定义了注册、订阅、查询三种操作方法。

  • RegistryService
public interface RegistryService {

    /**
     * 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则,等数据。
     * <p>
     * 注册需处理契约:<br>
     * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。<br>
     * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。<br>
     * 3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。<br>
     * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。<br>
     * 5. 允许URI相同但参数不同的URL并存,不能覆盖。<br>
     *
     * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     */
    void register(URL url);

    /**
     * 取消注册.
     * <p>
     * 取消注册需处理契约:<br>
     * 1. 如果是dynamic=false的持久存储数据,找不到注册数据,则抛IllegalStateException,否则忽略。<br>
     * 2. 按全URL匹配取消注册。<br>
     *
     * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     */
    void unregister(URL url);


    /**
     * 订阅符合条件的已注册数据,当有注册数据变更时自动推送.
     * <p>
     * 订阅需处理契约:<br>
     * 1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。<br>
     * 2. 当URL设置了category=routers,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。<br>
     * 3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&version=1.0.0<br>
     * 4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&group=*&version=*&classifier=*<br>
     * 5. 当注册中心重启,网络抖动,需自动恢复订阅请求。<br>
     * 6. 允许URI相同但参数不同的URL并存,不能覆盖。<br>
     * 7. 必须阻塞订阅过程,等第一次通知完后再返回。<br>
     *
     * @param url      订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     * @param listener 变更事件监听器,不允许为空
     */
    void subscribe(URL url, NotifyListener listener);

    /**
     * 取消订阅.
     * <p>
     * 取消订阅需处理契约:<br>
     * 1. 如果没有订阅,直接忽略。<br>
     * 2. 按全URL匹配取消订阅。<br>
     *
     * @param url      订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     * @param listener 变更事件监听器,不允许为空
     */
    void unsubscribe(URL url, NotifyListener listener);

    /**
     * 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,只返回一次结果。
     *
     * @param url 查询条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
     * @return 已注册信息列表,可能为空,含义同{@link com.alibaba.dubbo.registry.NotifyListener#notify(List<URL>)}的参数。
     * @see com.alibaba.dubbo.registry.NotifyListener#notify(List)
     */
    List<URL> lookup(URL url);

}
4.1、AbstractRegistry

com.alibaba.dubbo.registry.support.AbstractRegistry实现了Registry接口,而Registry接口继承了RegistryService接口,所以AbstractRegistry就是Registry的实现类,主要是对注册、订阅、通知和查询这类方法做出了本地的实现,即将各种注册、监听关系保存在了自身的属性之中:

4.1.1、属性及构造方法
// URL地址分隔符,用于文件缓存中,服务提供者URL分隔
    private static final char URL_SEPARATOR = ' ';
    // URL地址分隔正则表达式,用于解析文件缓存中服务提供者URL列表
    private static final String URL_SPLIT = "\\s+";
    // logger
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    // 本地配置缓存
    private final Properties properties = new Properties();
    // 注册中心缓存写入执行器,线程数=1
    private final ExecutorService registryCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveRegistryCache", true));
    // 是否同步保存文件
    private final boolean syncSaveFile;
    // 上一次缓存改变数据版本号
    private final AtomicLong lastCacheChanged = new AtomicLong();
    // 已注册的URL集合
    private final Set<URL> registered = new ConcurrentHashSet<URL>();
    // 已订阅的URL及监听器映射集合
    private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
    // 被通知的URL集合
    private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>();
    // 该注册中心的URL
    private URL registryUrl;
    // 本地缓存文件
    private File file;

    /*
    *   构造方法,传入URL
     */
    public AbstractRegistry(URL url) {
        // 设置registryUrl
        setUrl(url);
        // 获取是否同步保存缓存
        syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
        // 缓存文件路径名称
        String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache");
        File file = null;
        // 创建缓存文件
        if (ConfigUtils.isNotEmpty(filename)) {
            file = new File(filename);
            if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
                if (!file.getParentFile().mkdirs()) {
                    throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
                }
            }
        }
        this.file = file;
        // 加载配置到properties中
        loadProperties();
        // 通知所有订阅该URL的监听者
        notify(url.getBackupUrls());
    }
4.1.2、核心方法
    /*
    *   以下关于注册、监听的方法类似于观察者模式的实现
     */

    /*
    *   查询特定URL的全部通知列表
     */
    public List<URL> lookup(URL url) {
        // 订阅列表
        List<URL> result = new ArrayList<URL>();
        // 获取该url的通知列表
        Map<String, List<URL>> notifiedUrls = getNotified().get(url);
        if (notifiedUrls != null && notifiedUrls.size() > 0) {
            for (List<URL> urls : notifiedUrls.values()) {
                for (URL u : urls) {
                    if (!Constants.EMPTY_PROTOCOL.equals(u.getProtocol())) {
                        // 添加到result中
                        result.add(u);
                    }
                }
            }
        } else {
            // 通知列表为空,创建监听者做一次订阅操作,再获取到订阅列表
            final AtomicReference<List<URL>> reference = new AtomicReference<List<URL>>();
            NotifyListener listener = new NotifyListener() {
                public void notify(List<URL> urls) {
                    reference.set(urls);
                }
            };
            subscribe(url, listener); // Subscribe logic guarantees the first notify to return
            List<URL> urls = reference.get();
            if (urls != null && !urls.isEmpty()) {
                for (URL u : urls) {
                    if (!Constants.EMPTY_PROTOCOL.equals(u.getProtocol())) {
                        result.add(u);
                    }
                }
            }
        }
        return result;
    }
    /*
    *   注册URL
     */
    public void register(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("register url == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Register: " + url);
        }
        // 将URL添加到Set<URL>中
        registered.add(url);
    }

    /*
    *   取消注册URL
     */
    public void unregister(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("unregister url == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Unregister: " + url);
        }
        // 将URL从Set<URL>中移除
        registered.remove(url);
    }

    /*
    *   订阅,建立URL与监听者的映射关系
     */
    public void subscribe(URL url, NotifyListener listener) {
        if (url == null) {
            throw new IllegalArgumentException("subscribe url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("subscribe listener == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Subscribe: " + url);
        }
        Set<NotifyListener> listeners = subscribed.get(url);
        if (listeners == null) {
            subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
            listeners = subscribed.get(url);
        }
        listeners.add(listener);
    }
    /*
    *   取消订阅
     */
    public void unsubscribe(URL url, NotifyListener listener) {
        if (url == null) {
            throw new IllegalArgumentException("unsubscribe url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("unsubscribe listener == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Unsubscribe: " + url);
        }
        Set<NotifyListener> listeners = subscribed.get(url);
        if (listeners != null) {
            listeners.remove(listener);
        }
    }
    /*
    *   断线重连恢复
     */
    protected void recover() throws Exception {
        // 重新建立URL注册关系
        Set<URL> recoverRegistered = new HashSet<URL>(getRegistered());
        if (!recoverRegistered.isEmpty()) {
            if (logger.isInfoEnabled()) {
                logger.info("Recover register url " + recoverRegistered);
            }
            for (URL url : recoverRegistered) {
                register(url);
            }
        }
        // 重新建立订阅关系
        Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(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()) {
                    subscribe(url, listener);
                }
            }
        }
    }

    /*
    *   通知所有URL的所有监听者出现变更
     */
    protected void notify(List<URL> urls) {
        if (urls == null || urls.isEmpty()) return;

        for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
            URL url = entry.getKey();

            if (!UrlUtils.isMatch(url, urls.get(0))) {
                continue;
            }
            // 获取到监听者集合
            Set<NotifyListener> listeners = entry.getValue();
            if (listeners != null) {
                for (NotifyListener listener : listeners) {
                    try {
                        // 通知该url的该listener下urls的变更
                        notify(url, listener, filterEmpty(url, urls));
                    } catch (Throwable t) {
                        logger.error("Failed to notify registry event, urls: " + urls + ", cause: " + t.getMessage(), t);
                    }
                }
            }
        }
    }
    // 对特定的url做全量的变更通知
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        if ((urls == null || urls.isEmpty())
                && !Constants.ANY_VALUE.equals(url.getServiceInterface())) {
            logger.warn("Ignore empty notify urls for subscribe url " + url);
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
        }
        Map<String, List<URL>> result = new HashMap<String, List<URL>>();
        for (URL u : urls) {
            if (UrlUtils.isMatch(url, u)) {
                String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
                List<URL> categoryList = result.get(category);
                if (categoryList == null) {
                    categoryList = new ArrayList<URL>();
                    result.put(category, categoryList);
                }
                categoryList.add(u);
            }
        }
        if (result.size() == 0) {
            return;
        }
        Map<String, List<URL>> categoryNotified = notified.get(url);
        if (categoryNotified == null) {
            notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
            categoryNotified = notified.get(url);
        }
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            String category = entry.getKey();
            List<URL> categoryList = entry.getValue();
            categoryNotified.put(category, categoryList);
            saveProperties(url);
            listener.notify(categoryList);
        }
    }
    // 保存配置
    private void saveProperties(URL url) {
        if (file == null) {
            return;
        }

        try {
            StringBuilder buf = new StringBuilder();
            Map<String, List<URL>> categoryNotified = notified.get(url);
            if (categoryNotified != null) {
                for (List<URL> us : categoryNotified.values()) {
                    for (URL u : us) {
                        if (buf.length() > 0) {
                            buf.append(URL_SEPARATOR);
                        }
                        buf.append(u.toFullString());
                    }
                }
            }
            properties.setProperty(url.getServiceKey(), buf.toString());
            long version = lastCacheChanged.incrementAndGet();
            if (syncSaveFile) {
                doSaveProperties(version);
            } else {
                registryCacheExecutor.execute(new SaveProperties(version));
            }
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
    }

    /*
    *   关闭dubbo时做销毁
     */
    public void destroy() {
        if (logger.isInfoEnabled()) {
            logger.info("Destroy registry:" + getUrl());
        }
        // 取消全部登记URL的登记
        Set<URL> destroyRegistered = new HashSet<URL>(getRegistered());
        if (!destroyRegistered.isEmpty()) {
            for (URL url : new HashSet<URL>(getRegistered())) {
                if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
                    try {
                        unregister(url);
                        if (logger.isInfoEnabled()) {
                            logger.info("Destroy unregister url " + url);
                        }
                    } catch (Throwable t) {
                        logger.warn("Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
                    }
                }
            }
        }
        // 取消全部订阅者的订阅
        Map<URL, Set<NotifyListener>> destroySubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed());
        if (!destroySubscribed.isEmpty()) {
            for (Map.Entry<URL, Set<NotifyListener>> entry : destroySubscribed.entrySet()) {
                URL url = entry.getKey();
                for (NotifyListener listener : entry.getValue()) {
                    try {
                        unsubscribe(url, listener);
                        if (logger.isInfoEnabled()) {
                            logger.info("Destroy unsubscribe url " + url);
                        }
                    } catch (Throwable t) {
                        logger.warn("Failed to unsubscribe url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
                    }
                }
            }
        }
    }
4.2、FailbackRegistry

com.alibaba.dubbo.registry.support.FailbackRegistry ,实现 AbstractRegistry 抽象类,支持失败重试的 Registry 抽象类。

该类实现了注册中心所需的相关操作。当然,更进一步的话还是需要zk或者redis作为注册中心的实现,下文会提到

4.2.1、属性及构造方法
 /**
     * 定时任务执行器
     */
    // Scheduled executor service
    private final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryFailedRetryTimer", true));

    /**
     * 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
     */
    // Timer for failure retry, regular check if there is a request for failure, and if there is, an unlimited retry
    private final ScheduledFuture<?> retryFuture;
    /**
     * 失败发起注册失败的 URL 集合
     */
    private final Set<URL> failedRegistered = new ConcurrentHashSet<URL>();
    /**
     * 失败取消注册失败的 URL 集合
     */
    private final Set<URL> failedUnregistered = new ConcurrentHashSet<URL>();
    /**
     * 失败发起订阅失败的监听器集合
     */
    private final ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
    /**
     * 失败取消订阅失败的监听器集合
     */
    private final ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
    /**
     * 失败通知通知的 URL 集合
     */
    private final ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<URL, Map<NotifyListener, List<URL>>>();
    /**
     * 是否销毁
     */
    private AtomicBoolean destroyed = new AtomicBoolean(false);

    public FailbackRegistry(URL url) {
        super(url);
        // 重试频率,单位:毫秒
        int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
        // 创建失败重试定时器
        this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                // Check and connect to the registry
                try {
                    retry();
                } catch (Throwable t) { // Defensive fault tolerance
                    logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
                }
            }
        }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
    }
4.2.2、核心方法

主要注意的点在与真正完成到zk或redis中写入服务信息的方法是doRegister(URL url)这个方法,这个方法在本类中只给出了抽象,需要由后续的ZookeeperRegistry或RedisRegistry来实现具体的逻辑,这就是很常见的一种模板方法模式。具体的zk或redis实现会在后续的文章中给出。

/**
     * 添加到 `failedSubscribed`
     *
     * @param url
     * @param listener
     */
    private void addFailedSubscribed(URL url, NotifyListener listener) {
        Set<NotifyListener> listeners = failedSubscribed.get(url);
        if (listeners == null) {
            failedSubscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
            listeners = failedSubscribed.get(url);
        }
        listeners.add(listener);
    }

    /**
     * 移除出 `failedSubscribed` `failedUnsubscribed` `failedNotified`
     *
     * @param url URL
     * @param listener 监听器
     */
    private void removeFailedSubscribed(URL url, NotifyListener listener) {
        // 移除出 `failedSubscribed`
        Set<NotifyListener> listeners = failedSubscribed.get(url);
        if (listeners != null) {
            listeners.remove(listener);
        }
        // 移除出 `failedUnsubscribed`
        listeners = failedUnsubscribed.get(url);
        if (listeners != null) {
            listeners.remove(listener);
        }
        // 移除出 `failedNotified`
        Map<NotifyListener, List<URL>> notified = failedNotified.get(url);
        if (notified != null) {
            notified.remove(listener);
        }
    }

    @Override
    public void register(URL url) {
        // 已销毁,跳过
        if (destroyed.get()){
            return;
        }
        // 添加到 `registered` 变量
        super.register(url);
        // 移除出 `failedRegistered` `failedUnregistered` 变量
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        // 向注册中心发送注册请求
        try {
            // Sending a registration request to the server side
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // 如果开启了启动时检测,则直接抛出异常
            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol()); // 非消费者。消费者会在 `ReferenceConfig#createProxy(...)` 方法中,调用 `Invoker#avalible()` 方法,进行检查。
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 将失败的注册请求记录到 `failedRegistered`,定时重试
            // Record a failed registration request to a failed list, retry regularly
            failedRegistered.add(url);
        }
    }

    @Override
    public void unregister(URL url) {
        // 已销毁,跳过
        if (destroyed.get()){
            return;
        }
        // 移除出 `registered` 变量
        super.unregister(url);
        // 移除出 `failedRegistered` `failedUnregistered` 变量
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        // 向注册中心发送取消注册请求
        try {
            // Sending a cancellation request to the server side
            doUnregister(url);
        } catch (Exception e) {
            Throwable t = e;

            // 如果开启了启动时检测,则直接抛出异常
            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to unregister " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to uregister " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 将失败的取消注册请求记录到 `failedUnregistered`,定时重试
            // Record a failed registration request to a failed list, retry regularly
            failedUnregistered.add(url);
        }
    }

    @Override
    public void subscribe(URL url, NotifyListener listener) {
        // 已销毁,跳过
        if (destroyed.get()){
            return;
        }
        // 移除出 `subscribed` 变量
        super.subscribe(url, listener);
        // 移除出 `failedSubscribed` `failedUnsubscribed` `failedNotified`
        removeFailedSubscribed(url, listener);
        // 向注册中心发送订阅请求
        try {
            // Sending a subscription request to the server side
            doSubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;

            // 如果有缓存的 URL 集合,进行通知。后续订阅成功后,会使用最新的 URL 集合,进行通知。
            List<URL> urls = getCacheUrls(url);
            if (urls != null && !urls.isEmpty()) {
                notify(url, listener, urls);
                logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
            } else {
                // 如果开启了启动时检测,则直接抛出异常
                // If the startup detection is opened, the Exception is thrown directly.
                boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                        && url.getParameter(Constants.CHECK_KEY, true);
                boolean skipFailback = t instanceof SkipFailbackWrapperException;
                if (check || skipFailback) {
                    if (skipFailback) {
                        t = t.getCause();
                    }
                    throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
                } else {
                    logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
                }
            }

            // 将失败的订阅请求记录到 `failedSubscribed`,定时重试
            // Record a failed registration request to a failed list, retry regularly
            addFailedSubscribed(url, listener);
        }
    }

    @Override
    public void unsubscribe(URL url, NotifyListener listener) {
        // 已销毁,跳过
        if (destroyed.get()){
            return;
        }
        // 移除出 `unsubscribed` 变量
        super.unsubscribe(url, listener);
        // 移除出 `failedSubscribed` `failedUnsubscribed` `failedNotified`
        removeFailedSubscribed(url, listener);
        // 向注册中心发送取消订阅请求
        try {
            // Sending a canceling subscription request to the server side
            doUnsubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;

            // 如果开启了启动时检测,则直接抛出异常
            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true);
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to unsubscribe " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 将失败的订阅请求记录到 `failedUnsubscribed`,定时重试
            // Record a failed registration request to a failed list, retry regularly
            Set<NotifyListener> listeners = failedUnsubscribed.get(url);
            if (listeners == null) {
                failedUnsubscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
                listeners = failedUnsubscribed.get(url);
            }
            listeners.add(listener);
        }
    }

    @Override
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        // 通知监听器
        try {
            doNotify(url, listener, urls);
        } catch (Exception t) {
            // 将失败的通知记录到 `failedNotified`,定时重试
            // Record a failed registration request to a failed list, retry regularly
            Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
            if (listeners == null) {
                failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
                listeners = failedNotified.get(url);
            }
            listeners.put(listener, urls);
            logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
        }
    }

    protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
        super.notify(url, listener, urls);
    }

    @Override
    protected void recover() throws Exception {
        // register 恢复注册,添加到 `failedRegistered` ,定时重试
        Set<URL> recoverRegistered = new HashSet<URL>(getRegistered());
        if (!recoverRegistered.isEmpty()) {
            if (logger.isInfoEnabled()) {
                logger.info("Recover register url " + recoverRegistered);
            }
            for (URL url : recoverRegistered) {
                failedRegistered.add(url);
            }
        }
        // subscribe 恢复订阅,添加到 `failedSubscribed` ,定时重试
        Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(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()) {
                    addFailedSubscribed(url, listener);
                }
            }
        }
    }

    /**
     * 重试
     */
    // Retry the failed actions
    protected void retry() {
        // 重试执行注册
        if (!failedRegistered.isEmpty()) {
            Set<URL> failed = new HashSet<URL>(failedRegistered); // 避免并发冲突
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry register " + failed);
                }
                try {
                    for (URL url : failed) {
                        try {
                            // 执行注册
                            doRegister(url);
                            // 移除出 `failedRegistered`
                            failedRegistered.remove(url);
                        } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                            logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                        }
                    }
                } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                    logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        // 重试执行取消注册
        if (!failedUnregistered.isEmpty()) {
            Set<URL> failed = new HashSet<URL>(failedUnregistered); // 避免并发冲突
            if (!failed.isEmpty()) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry unregister " + failed);
                }
                try {
                    for (URL url : failed) {
                        try {
                            // 执行取消注册
                            doUnregister(url);
                            // 移除出 `failedUnregistered`
                            failedUnregistered.remove(url);
                        } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                            logger.warn("Failed to retry unregister  " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                        }
                    }
                } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                    logger.warn("Failed to retry unregister  " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        // 重试执行注册
        if (!failedSubscribed.isEmpty()) {
            Map<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedSubscribed); // 避免并发冲突
            for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, 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<URL, Set<NotifyListener>> entry : failed.entrySet()) {
                        URL url = entry.getKey();
                        Set<NotifyListener> listeners = entry.getValue();
                        for (NotifyListener listener : listeners) {
                            try {
                                // 执行注册
                                doSubscribe(url, listener);
                                // 移除出监听器
                                listeners.remove(listener);
                            } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                                logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                            }
                        }
                    }
                } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                    logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        // 重试执行取消注册
        if (!failedUnsubscribed.isEmpty()) {
            Map<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedUnsubscribed);
            for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(failed).entrySet()) {
                if (entry.getValue() == null || entry.getValue().isEmpty()) {
                    failed.remove(entry.getKey());
                }
            }
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry unsubscribe " + failed);
                }
                try {
                    for (Map.Entry<URL, Set<NotifyListener>> entry : failed.entrySet()) {
                        URL url = entry.getKey();
                        Set<NotifyListener> listeners = entry.getValue();
                        for (NotifyListener listener : listeners) {
                            try {
                                // 执行取消注册
                                doUnsubscribe(url, listener);
                                // 移除出监听器
                                listeners.remove(listener);
                            } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                                logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                            }
                        }
                    }
                } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                    logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        // 重试执行通知监听器
        if (!failedNotified.isEmpty()) {
            Map<URL, Map<NotifyListener, List<URL>>> failed = new HashMap<URL, Map<NotifyListener, List<URL>>>(failedNotified);
            for (Map.Entry<URL, Map<NotifyListener, List<URL>>> entry : new HashMap<URL, Map<NotifyListener, List<URL>>>(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, List<URL>> values : failed.values()) {
                        for (Map.Entry<NotifyListener, List<URL>> entry : values.entrySet()) {
                            try {
                                NotifyListener listener = entry.getKey();
                                List<URL> urls = entry.getValue();
                                // 通知监听器
                                listener.notify(urls);
                                // 移除出监听器
                                values.remove(listener);
                            } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                                logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                            }
                        }
                    }
                } catch (Throwable t) { // Ignore all the exceptions and wait for the next retry
                    logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
    }
菜鸟学Dubbo和Nacos随记(更新中) Dubbo和Nacos是两个在微服务架构中常用的工具,它们各自扮演着不同的角色。Dubbo主要关注于RPC框架的实现,提供了高性能的远程方法调用和服务治理功能;而Nacos则更侧重于服务发现、配置管理和服务管理平台的建设。Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架。它允许应用通过高性能的RPC实现服务的输出和输入功能,并可以与Spring框架无缝集成。 阅读详情

相关推荐

Dubbo源码解析:注册中心

介绍 Dubbo可以用zookeeper,reids,nacos来实现注册中心 Registry继承了RegistryService接口,RegistryService接口定义了对注册中心的基本操作 public interface RegistryService { // 注册 void register(URL url); // 注销 void unregister(URL url); // 订阅 void subscribe(URL url, No

小识的博客 672

SpringBoot与Dubbo整合报错排查(Nacos作为注册中心)

异常信息: java.lang.IllegalStateException: No such extension org.apache.dubbo.registry.RegistryFactory by name nacos 查看SpringBoot与Dubbo整合依赖的jar包 <dependency> <groupId>org.apache.dubbo&...

天涯芳草 1万+

fastdfs与dubbo整合实现分布式服务接口

fastdfs开源的轻量级分布式文件系统,它给我们提供了java版本的client api。通过该API可以实现对文件的上传、下载和删除。 为了避免每个应用都配置fasdtfs参数、读取配置文件,调用client api获取trackerServer、StorageServer然后再近些上传、下载 、删除等相关的调用和返回结果的处理。所以采用与dubbo整合,提供分布式服务接口,其它应用只需调用相应接口就可以了。

Dubbo笔记衍生篇②:Dubbo SPI 原理

一、前言 Dubbo 为每一个功能点提供了一个SPI 扩展接口。而为了为了不加载不使用SPI 实例,Dubbo通过适配器模式和动态编译技术,加载指定的SPI 实现。 二、Dubbo适配器原理 二、动态编译 众所周知,Java 程序想要运行首先需要将Java源代码编译成字节码文件,然后JVM把字节码文件加载到内存创建Class 对象后,使用Class对象创建实例。 正常情况下,我们是将所有源文件编译为字节码文件,然后由JVM统一加载,而动态编译则是在JVM进程运行时把源文件编译为字节码文件,然后使用字节码文件

猫吻鱼的博客 3929

告别编译噩梦:Vcpkg+Cmake一站式集成Tesseract-OCR C++开发环境(Windows)

本文详细介绍了如何在Windows系统上使用Vcpkg和CMake快速集成Tesseract-OCR C++开发环境,解决传统编译过程中的依赖问题。通过一键安装和自动化配置,开发者可以高效搭建OCR识别系统,支持中英文识别,并提供了常见问题的解决方案和进阶技巧。

weixin_29009339的博客 231

MDK5 Debug调试方法总结

MDK5 Debug调试方法总结

m0_70748084的博客 2万+

Dubbo底层源码解析

目录 1、Dubbo核心模块职责介绍 2、源码基础 2.1 SPI自适应 2.2 Netty实现简易Dubbo 3、标签解析 3.1 标签属性配置 3.2 配置渊源 4、服务暴露 4.1 服务暴露启动逻辑 4.2 参数检查 4.3 组装url 4.4 服务暴露分类 4.5 服务暴露 4.6 服务注册 5、服务发现 5.1 服务发现原理 5.2 服务引用入口 5.3 创建代理 5.4 代理调用 1、Dubbo核心模块职责介绍 Dubbo发展到现在,已经有3.0版.

长沙老码农 3945

逆向工程实战:如何一步步破解京东APP的sign签名算法(附Frida脚本)

本文详细解析了京东APP的sign签名算法逆向工程实战,通过Frida脚本动态分析Java层和Native层的多层签名机制,揭示商业应用的加密协议设计。文章提供了完整的逆向工程方法论与工具链,帮助开发者深入理解移动应用安全防护体系,提升安全防御能力。

weixin_28622215的博客 919

【021】Dubbo3从0到1系列之注册中心源码解析

本文解析了Dubbo注册中心的核心机制与源码实现。注册中心(Registry)作为微服务架构的关键组件,通过本地客户端与Zookeeper/Nacos等集群同步信息,实现服务提供者(Provider)和消费者(Consumer)的相互感知。源码分析显示,注册模块在Dubbo中相对独立,采用Node接口抽象各类节点(Provider/Consumer/注册中心节点),该接口定义了获取URL、检查可用性和销毁节点三个基础方法。

编程技术圈 905

Motoman机器人离线编程——Motocom32开发简介

欢迎关注Jungle的Motoman机器人离线编程专栏系列文章: Motoman机器人离线编程——Motocom32开发简介 Motoman机器人离线编程——Visio Studio环境配置 Motoman机器人离线编程——Motocom32库函数的使用与封装 Motoman机器人离线编程——Motocom32参考文献及注意事项 Motoman机器人离线编程——MotocomES介绍 ...

冯Jungle的个人博客 8714

Microsoft Dynamics 365软件二次开发:Dynamics365部署与版本控制策略

在Microsoft Dynamics 365中,自定义实体允许开发者扩展系统以满足特定业务需求。自定义实体可以包含字段、选项集、关系和其他属性,与标准实体类似,但完全由用户定义。1. 登录到Dynamics 365环境。2. 转到“解决方案”区域,选择“自定义”。3. 在“自定义”菜单中,选择“实体”。4. 点击“新建”,输入实体名称,例如“客户反馈”。5. 添加字段,如“反馈类型”、“反馈日期”、“描述”等。6. 设置主键和唯一标识符。7. 定义关系,如与“客户”实体的关联。

kkchenjj的博客 2209

深度解析dubbo源码系列

以下文章均基于dubbo v2.6.1 持续更新中… dubbo spi 扩展技术,框架基石 《深度解析dubbo扩展技术dubbo spi(注解)》 《深度解析dubbo扩展技术dubbo spi(实现二)》 《深度解析dubbo扩展技术dubbo spi(自适应实现)》 《深度解析dubbo扩展技术dubbo spi(自动激活实现)》 服务暴露 《深度解析dubbo服务本地暴露(injvm)》 《深度解析dubbo服务远程暴露(一)》 《深度解析dubbo服务远程暴露(二)》 《深度解析dubbo

猿上生活 1万+

基于Abaqus的横向各向同性线弹性理想塑性材料模型的umat子程序实现——以Fortran为例,带您揭秘弹性塑性模型编程之道

通过以上步骤,我们已经在Abaqus中使用Fortran实现了横向各向同性线弹性理想塑性材料模型的umat子程序。在实际应用中,我们需要根据具体的材料特性和加载条件来调整模型参数和计算过程。尽管我在这篇文章中尽力提供了详细的教程,但由于我自身的知识限制和经验不足,可能会有误解或错误。我希望大家在学习过程中能多思考,多实践,多交流,以更好地理解和掌握这个主题。

qq_38334677的博客 1690

Dubbo源码(4)-Zookeeper注册中心源码解析

文章目录一、Zookeeper在Dubbo中的作用1.1、什么是Zookeeper1.2、Dubbo的Zookeeper注册中心二、Zookeeper注册中心源码解析2.1、FailbackRegistry2.2、ZookeeperRegistryFactory2.3、ZookeeperRegistry2.3.1、属性及构造方法2.3.2、重点方法2.3.2.1、doRegister2.3.2.2...

BoringError的博客 1652

基于知识图谱的农作物病虫害问答系统设计与实现

基于规则的问题分类是自问答系统诞生以来就一直存在的方式,主要代表就是众多的专家系统,构建专家系统采用的是规则分类器,优点是能够对特定领域、特征明显的问题进行高效精准问答,但是。,在问答系统中,问题以语音及文本字符等自然语言形式输入,利用问答系统的内部的逻辑处理模块,通过自然语言处理技术获得输入问句的详细语义信息,再经过知识检索、置信分析等步骤,自然语言处理中,用于文本分类任务的机器学习方式有两种,一种用传统的机器学习,如 NBC、SVM 等,另一种采用深度学习,如 CNN 和 RNN 等。

全网粉丝30W+,优快云、稀土掘金人工智能领域签约作者,人工智能、大模型、Python 爬虫,商务合作,请私信沟通。 2166

有趣的方式学习DDR5(4)—— DDR5 SDRAM 启动与初始化过程全攻略

🟢。

weixin_43199439的博客 1624

Dubbo源码解析(一)---架构概述

一 前言 之前的文章描述了如何简单的搭建dubbo服务,会用只是迈入了初级阶段。要想更好的理解原理,只能通过源码。为什么要看源码呢?因为好的源码是一些大牛级别的工程师呕心沥血的结晶,这里面渗透着好多东西,比如设计模式,算法,以及我们可以更清楚的从开发者的角度去理解代码。所以在接下来的时间里会记录些dubbo核心源码的心得。 二 dubbo核心模块 核心模块职责介绍: ...

方圆几里的博客 8347

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

深度解析dubbo注册中心zookeeper实现

本文基于dubbo 2.6.x 文章目录一、ZookeeperRegistryFactory二、ZookeeperRegistry2.1 class与成员变量2.2 构造2.3 doRegister2.4 doUnregister2.5 doSubscribe2.6 doUnsubscribe三、总结 一、ZookeeperRegistryFactory ZookeeperRegistryFactory是注册中心zookeeper工厂的实现,代码比较简单,我们直接来看下。 可以看到ZookeeperRe

猿上生活 1813

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

精尽 Dubbo 源码分析 —— 注册中心抽象 API

1. 概述 本文分享 dubbo-registry-api 模块,注册中心模块:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。 2. 抽象 API 2.1 RegistryFactory 注册中心工厂接口,代码如下: /** * 注册中心工厂 */ @SPI("dubbo") public interface RegistryFactory { /** * 连...

qq_32782279的博客 326

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

DUBBO源码学习(二)注册中心源码解析

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

lisyee的博客 1109

Dubbo 3.2 源码导读

Dubbo 是一个高性能的 Java RPC 框架,广泛用于构建分布式服务。Dubbo 3.2 版本引入了一些新的特性和改进,是一个值得深入研究的版本。以下是对 Dubbo 3.2 源码的导读,帮助你理解其架构和设计。

大道至简 1273

dubbo源码解析-zookeeper连接

Dubbo源码解析系列文章均来自肥朝简书 前言 上周的dubbo源码解析-远程暴露中讲解了远程暴露的大致过程,但是期间大家也发现了,这个过程涉及到了很多分支,比如netty,zookeeper.当然设计模式就不说了,这个贯穿在整个框架的始终. 但是我也认为,好的源码分析类文章,应该是先整体,后枝干的,就比如如果我们一开始学习HTML的时候先把各个标签过一遍,估计可能很多人还没入门就放弃了,比...

Franco的博客 469

Dubbo注册中心(ZooKeeper)

文章目录1. 注册中心概述2. 注册中心的工作流程3. 注册中心的原理3.1 ZooKeeper做注册中心的原理 1. 注册中心概述 在Dubbo微服务体系中,注册中心是其核心组件之一。Dubbo通过注册中心实现了分布式环境中各服务之间的注册与发现,是各个分布式节点之间的纽带。其主要作用如下: 动态加入(服务注册/暴露):一个服务提供者通过注册中心可以动态地把自己暴露给其他消费者,无须消费者逐个...

xingze的博客 1149

【dubbo源码解析】--- dubbo的服务注册与发现机制底层原理探析

本文对应源码地址:https://github.com/nieandsun/dubbo-study 文章目录1 dubbo的RPC远程调用整体过程简单回顾 1 dubbo的RPC远程调用整体过程简单回顾 上篇文章《【dubbo源码解析】— dubbo的服务暴露+服务消费(RPC调用)底层原理深入探析》非常详细的介绍了dubbo的RPC远程调用过程,这里做一个简单的回顾。 【首先】 需要明确的是,在dubbo的RPC远程调用过程中,最核心的动作是如下两个: (1)protocol. export(.

nrsc 2786

Dubbo之注册中心

目录dubbo版本注册中心直连模式zookeeper注册中心相关配置多注册中心只订阅不注册只注册不订阅源码位置源码分析(Zookeeper为例)注册订阅dubbo版本 dubbo版本2.6.7 注册中心 Dubbo通过注册中心实现了各服务之间的注册与发现 动态加入:provider通过注册中心动态暴露服务给consumer 动态发现:一个消费者可以动态感知新的配置、路由规则、新的cons...

jannal专栏 146

【024】Dubbo3从0到1系列之Dubbo注册中心AbstractRegistry【下】

本文分析了Dubbo3中AbstractRegistry的核心实现,主要内容包括: 核心流程:详细解析了初始化、注册、订阅、通知和销毁五大核心流程的实现机制 设计特点:抽象基类、SPI支持、线程安全、容错设计和性能优化等特性 本地缓存机制:重点分析了内存与磁盘的双重缓存架构,包括: 缓存数据结构与服务键映射 异步保存与文件锁机制 版本控制与重试策略 双重读取策略(内存优先) notify方法实现:详细说明URL匹配机制、分类通知和缓存更新流程 整体展现了Dubbo注册中心模块的高可靠性设计 ....

编程技术圈 952
上一篇: Dubbo源码(2)-动态编译技术源码解析
下一篇: Dubbo源码(4)-Zookeeper注册中心源码解析
BoringRong
BoringRong 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 2万+粉丝 386原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值