学习dubbo(2):dubbo注册中心

学习《深入理解apache dubbo与实战》

总结:这一章,没啥内容,只是简单的介绍了一下dubbo实现注册中心,怎么使用zk,概念介绍,然后贴源码,虽然这注册中心其实没啥东西,无非就是讲服务注册到一个地方,然后好治理分配,调用这些服务,一个服务一个节点,对比Springcloud的注册中心,其实原理思想还是很多相同的

1,注册中心作用

1)动态加入,一个服务提供者通过注册中心可以动态把自己暴露给别人用,无须消费者一个个去更新配置文件。

2)动态发现,一个消费者可以动态的感知新的配置,路由规则和新的服务提供者,无须重启。

3)动态调整,注册中心支持参数的动态调整,新参数自动更新到所有相关服务节点

4)统一配置,避免了本地配置导致每个服务的配置不一致问题

dubbo主要有四种注册中心:zk,redis,simple,multicast,当然主流推荐的是zk。

2,zk

zk本身功能很强大,用于配置管理,名字服务,提供分布式同步以及集群管理。

书上其实基本没介绍过zk,网上有很多详细介绍,

zk介绍:https://blog.csdn.net/java_66666/article/details/81015302

zk到底是干嘛的:https://www.cnblogs.com/ultranms/p/9585191.html

zk是树形结构的注册中心,每个节点的类型分为持久节点,持久顺序节点,临时节点,和临时顺序节点。

持久节点:服务注册后保证节点不会丢失,注册中心重启也会存在

持久顺序节点:在持久节点的特性的基础上增加了节点先后顺序的能力

临时节点:服务注册后连接丢失或者session超时,注册的节点会自动移除

临时顺序节点:看题意增加了顺序的能力

但是dubbo使用zk时,只有持久和临时节点,

dubbo在启动的时候注册中心会有四个目录:服务提供者目录providers,服务消费者目录coumer,路由配置目录routers,动态配置目录configurators

2.1 发布的实现

    //zkClient类,如果我没找错的话,下面这两个方法就是注册与取消的,具体不清楚
    //将服务注册到注册中心上去
    //取消发布
    @Override
    public void delete(String path){
        //never mind if ephemeral
        persistentExistNodePath.remove(path);
        deletePath(path);
    }

    //发布
    @Override
    public void create(String path, boolean ephemeral) {
        if (!ephemeral) {
            if(persistentExistNodePath.contains(path)){
                return;
            }
            if (checkExists(path)) {
                persistentExistNodePath.add(path);
                return;
            }
        }
        int i = path.lastIndexOf('/');
        if (i > 0) {
            create(path.substring(0, i), false);
        }
        if (ephemeral) {
            createEphemeral(path);
        } else {
            createPersistent(path);
            persistentExistNodePath.add(path);
        }
    }

2.2 订阅

订阅通常有pull和push两种方式,一种是客户端定时轮询注册中心拉去配置,一种是注册中心主动推送数据给客户端,目前dubbo采用的是定时轮询拉去。

在服务暴露时,服务端会订阅configurators用于监听动态配置,消费端启动时,消费端会订阅providers,routers和configurators三个目录,

zk注册中心采用的是事件通知+客户端拉取,客户端在第一次连接时,会获取对应目录下全量数据,并在订阅的节点上注册一个watcher,客户端与注册中心之间保持tcp长连接,只要有数据或者节点版本号变化,注册中心会根据watcher回调主动通知客户端,客户端再次拉取全量数据

全量订阅服务源码:

看不大懂,基本原理就是从注册中心上获取全量的服务信息,前面提到的四个目录中的东西,缓存到本地,防止一直请求注册中心,给注册中心压力,这就是下面说的缓存机制,我看的版本本地缓存只是缓存到同步map中,书上写的还有一份本地文件存在磁盘,但是我看的版本源码没有。

public void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            if (ANY_VALUE.equals(url.getServiceInterface())) {
                String root = toRootPath();
                ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
//listeners为空,说明缓存里没有,则将listeners放入缓存
                if (listeners == null) {
                    zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                    listeners = zkListeners.get(url);
                }
                ChildListener zkListener = listeners.get(listener);
                if (zkListener == null) {
                    listeners.putIfAbsent(listener, (parentPath, currentChilds) -> {
                        for (String child : currentChilds) {
                            child = URL.decode(child);
                            if (!anyServices.contains(child)) {
                                anyServices.add(child);
                                subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
                                        Constants.CHECK_KEY, String.valueOf(false)), listener);
                            }
                        }
                    });
                    zkListener = listeners.get(listener);
                }
                zkClient.create(root, false);
                List<String> services = zkClient.addChildListener(root, zkListener);
                if (CollectionUtils.isNotEmpty(services)) {
                    for (String service : services) {
                        service = URL.decode(service);
                        anyServices.add(service);
                        subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
                                Constants.CHECK_KEY, String.valueOf(false)), listener);
                    }
                }
            } else {
                List<URL> urls = new ArrayList<>();
                for (String path : toCategoriesPath(url)) {
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                        listeners = zkListeners.get(url);
                    }
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
                        listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                        zkListener = listeners.get(listener);
                    }
                    zkClient.create(path, false);
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

3,redis实现

4,缓存

消费者或者治理中心获取注册信息后会做本地缓存,防止注册中心压力过大,缓存在一个

ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>>中

5,重试机制

在org.apache.dubbo.registry.support.FailbackRegistry类中定义了很多个集合,用于保存重试:

private final ConcurrentMap<URL, FailedRegisteredTask> failedRegistered = new ConcurrentHashMap<URL, FailedRegisteredTask>();

    private final ConcurrentMap<URL, FailedUnregisteredTask> failedUnregistered = new ConcurrentHashMap<URL, FailedUnregisteredTask>();

    private final ConcurrentMap<Holder, FailedSubscribedTask> failedSubscribed = new ConcurrentHashMap<Holder, FailedSubscribedTask>();

    private final ConcurrentMap<Holder, FailedUnsubscribedTask> failedUnsubscribed = new ConcurrentHashMap<Holder, FailedUnsubscribedTask>();

    private final ConcurrentMap<Holder, FailedNotifiedTask> failedNotified = new ConcurrentHashMap<Holder, FailedNotifiedTask>();

6,设计模式

整个注册中心的逻辑部分使用了模版模式

注册中心实现用的是工厂模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值