Dubbo源码分析----暴露服务

本文深入分析了Dubbo服务的暴露过程,包括本地暴露和远程暴露。在本地暴露中,使用JdkProxyFactory创建Invoker并经过多个filter,通过InjvmProtocol进行暴露。远程暴露涉及RegistryProtocol,首先通过ZookeeperRegistry进行注册中心初始化,包括加载缓存、更新缓存、定时重试和初始化Zookeeper通信。然后执行register方法在Zookeeper上创建节点,订阅方法处理服务订阅与通知。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暴露服务的过程中,会涉及到两个Protocol
1. DubboProtocol主要是做网络通信相关初始化
2. RegistryProtocol主要是做zk的注册和订阅相关

在提供一个服务的时候,需要在配置文件里声明如下xml

<dubbo:service....

    然后Spring会根据对应关系执行对应的BeanDefinitionParser,然后实例化对应的类,提供一个服务的时候会实例化ServiceBean(具体对应关系看DubboNamespaceHandler类;spring解析自定义标签可以看下spring源码关于标签的处理,这里就不说了)

    ServiceBean实现了InitializingBean和ApplicationContextAware接口,所以会执行afterPropertiesSet和onApplicationEvent方法,这里就是入口,然后就会执行export方法暴露服务

    一路跟下去,都是设置一下属性值,然后到了doExportUrls方法便开始主要的逻辑

    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);//获取注册中心的url
        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

    在Zookeeper为注册中心的情况下,registryURLs值如下
[registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=test&dubbo=2.0.0&owner=william&pid=4444&registry=zookeeper&timestamp=1488886235790]

    进入doExportUrlsFor1Protocol方法,看下暴露服务的主要逻辑

        if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {

            //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
            if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }
            }
        }

1.本地暴露

    会先使用exportLocal暴露本地服务

    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(NetUtils.LOCALHOST)
                    .setPort(0);
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
        }
    }

    看了Dubbo的扩展机制会知道,ProxyFactory默认会使用接口上@Spi注解声明的服务,为了容易理解,我把注解上的@SPI设置成jdk,那么就会使用jdk对应的实现类,即JdkProxyFactory
    JdkProxyFactory的getInvoker方法如下:

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, 
                                      Class<?>[] parameterTypes, 
                                      Object[] arguments) throws Throwable {
                Method method = proxy.getClass().getMethod(methodName, parameterTypes);
                return method.invoke(proxy, arguments);
            }
        };
    }

    返回一个AbstractProxyInvoker类的对象,这个AbstractProxyInvoker主要是接收消费方的请求后,执行本地方法的一个Invoker,其中是使用了反射机制来调用了本地方法
    获取到Invoker之后,需要使用Protocol的export来暴露这个服务,在讲Dubbo扩展机制的时候,Protocol外面有两个装饰类,那么export会先调用ProtocolListenerWrapper

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return new ListenerExporterWrapper<T>(protocol.export(invoker), 
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    }

    这时的url为
injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=test&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=4444&side=provider&timestamp=1488887324869
    对象为
image.png
    那么if条件不满足,将调用ProtocolFilterWrapper的export方法

    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

    If条件还是不满足,执行下面的代码
    注意:这里的protocol就是InjvmProtocol了

    然后看下buildInvokerChain方法,这个方法建立了一个个的filter,使用了责任链模式,一个普通的Invoker调用也会经历这些filter,每个filter都有自己特殊的功能

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i --) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {
                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }
                    public URL getUrl() {
                        return invoker.getUrl();
                    }
                    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值