Dubbo之Protocol

dubbo版本

  1. dubbo版本2.6.7

Protocol

  1. Protocol层主要包含ProtocolFilterExporterListenerInvokerListenerProtocol层是RPC的核心调用层,具体的RPC协议都可以通过Protocol扩展。Protocol接口是入口,主要用来封装Exporter和Invoker的方法,Protocol#export将服务暴露的处理过程,Invoker标识服务远程代理对象

    /**
     * Protocol. (API/SPI, Singleton, ThreadSafe)
     */
    @SPI("dubbo")
    public interface Protocol {
    
        /**
         *当用户没有配置端口时获取默认端口
         *
         * @return default port
         */
        int getDefaultPort();
    
        /**
         * 暴露远程服务: <br>
         * 1. 协议在接收请求时,应记录请求来源方地址信息:
         * RpcContext.getContext().setRemoteAddress();<br>
         * 2. export() 必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别<br>
         * 3. 传入的Invoker由框架实现并传入,协议不需要关心<br>
         *
         * @param <T>     服务类型
         * @param invoker 服务调用者
         * @return 暴露服务的引用,用于之后取消暴露
         * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
         */
        @Adaptive
        <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    
        /**
         * 引用远程服务: <br>
         * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法<br>
         * 2. 返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求 <br>
         * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复
         *
         * @param <T>  服务的类型
         * @param type 服务的类
         * @param url  远程服务的URL地址
         * @return invoker 服务的本地代理
         * @throws RpcException 当连接服务提供方失败时抛出
         */
        @Adaptive
        <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    
        /**
         * 销毁协议: <br>
         * 1. 取消该协议所有已经暴露和引用的服务<br>
         * 2. 释放协议所占用的所有资源,比如连接和端口. <br>
         * 3. 协议在释放后,依然能暴露和引用新的服务
         */
        void destroy();
    
    }
  2. AbstractProtocol代码

    public abstract class AbstractProtocol implements Protocol {
    
        protected final Logger logger = LoggerFactory.getLogger(getClass());
        //服务暴露者集合
        protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
    
        //TODO SOFEREFENCE 服务引用者集合
        protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
    
        //获取ServiceKey:group0/com.alibaba.dubbo.demo.DemoService:1.0.0:20882
        // group+"/"+serviceName+":"+serviceVersion+":"+port
        protected static String serviceKey(URL url) {
            int port = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
            return serviceKey(port, url.getPath(), url.getParameter(Constants.VERSION_KEY),
                    url.getParameter(Constants.GROUP_KEY));
        }
    		...省略...
    }
  3. Protocol适配器代码

    /*
     * Decompiled with CFR.
     *
     * Could not load the following classes:
     *  com.alibaba.dubbo.common.URL
     *  com.alibaba.dubbo.common.extension.ExtensionLoader
     *  com.alibaba.dubbo.rpc.Exporter
     *  com.alibaba.dubbo.rpc.Invoker
     *  com.alibaba.dubbo.rpc.RpcException
     */
    package com.alibaba.dubbo.rpc;
    
    import com.alibaba.dubbo.common.URL;
    import com.alibaba.dubbo.common.extension.ExtensionLoader;
    import com.alibaba.dubbo.rpc.Exporter;
    import com.alibaba.dubbo.rpc.Invoker;
    import com.alibaba.dubbo.rpc.Protocol;
    import com.alibaba.dubbo.rpc.RpcException;
    
    public class Protocol$Adaptive
            implements Protocol {
        @Override
        public void destroy() {
            throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
        }
    
        @Override
        public int getDefaultPort() {
            throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
        }
    
        public Invoker refer(Class clazz, URL uRL) throws RpcException {
            String string;
            if (uRL == null) {
                throw new IllegalArgumentException("url == null");
            }
            URL uRL2 = uRL;
            String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
            if (string == null) {
                throw new IllegalStateException(new StringBuffer().append("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(").append(uRL2.toString()).append(") use keys([protocol])").toString());
            }
            Protocol protocol = (Protocol) ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
            return protocol.refer(clazz, uRL);
        }
    
        public Exporter export(Invoker invoker) throws RpcException {
            String string;
            if (invoker == null) {
                throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
            }
            if (invoker.getUrl() == null) {
                throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
            }
            URL uRL = invoker.getUrl();
            String string2 = string = uRL.getProtocol() == null ? "dubbo" : uRL.getProtocol();
            if (string == null) {
                throw new IllegalStateException(new StringBuffer().append("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(").append(uRL.toString()).append(") use keys([protocol])").toString());
            }
            Protocol protocol = (Protocol) ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
            return protocol.export(invoker);
        }
    }

DubboProtocol

  1. Dubbo协议在客户端和服务端之间是通过TCP长连接通信的,默认情况下只会维护一个长连接,但是如果在引用服务的时候在消费方(或服务提供方)配置了连接数(connections配置),那么Dubbo会为配置的那个service单独维护一个连接集合。而没有配置连接的则统一共享一个TCP连接。

    <dubbo:reference interface="com.foo.BarService" connections="10" /> 
    <dubbo:service interface="com.foo.BarService" connections="10" />
    都配置以<dubbo:reference/>优先
  2. DubboProtocol#export:就是启动服务端端口

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        //获取Provider的URL
        URL url = invoker.getUrl();
    
        // export service.
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);
    
        //export an stub service for dispatching event
        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackservice) {
            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                if (logger.isWarnEnabled()) {
                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                            "], has set stubproxy support event ,but no stub methods founded."));
                }
            } else {
                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
            }
        }
    
        openServer(url);
        optimizeSerialization(url);
        return exporter;
    }

RegistryProtocol

  1. RegistryProtocol#export:服务暴露

    @Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //export invoker 打开端口,将服务存储到map中
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
        //获取注册中心URL
        //zookeeper://dubbo-zookeeper:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F10.0.75.1%3A20882%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bean.name%3Dcom.alibaba.dubbo.demo.DemoService%26bind.ip%3D10.0.75.1%26bind.port%3D20882%26dubbo%3D2.0.2%26generic%3Dfalse%26group%3Dgroup0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D67364%26qos.port%3D22223%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1626093287774%26version%3D1.0.0&pid=67364&qos.port=22223&timestamp=1626093287757
        URL registryUrl = getRegistryUrl(originInvoker);
    
        //registry provider 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);
        //获取已注册的服务提供者 URL dubbo://10.0.75.1:20882/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=com.alibaba.dubbo.demo.DemoService&dubbo=2.0.2&generic=false&group=group0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=67488&revision=1.0.0&side=provider&timestamp=1626093385765&version=1.0.0
        final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);
    
        //to judge to delay publish whether or not  获取register参数,判断是否是延迟发布
        boolean register = registeredProviderUrl.getParameter("register", true);
        // 向服务提供者与消费者注册表中注册服务提供者
        ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
        // 根据 register 的值决定是否注册服务
        if (register) {
            // 向注册中心注册服务元数据
            register(registryUrl, registeredProviderUrl);
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }
    
        // Subscribe the override data  订阅override数据
        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
        //provider://10.0.75.1:20882/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=com.alibaba.dubbo.demo.DemoService&category=configurators&check=false&dubbo=2.0.2&generic=false&group=group0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=67488&revision=1.0.0&side=provider&timestamp=1626093385765&version=1.0.0
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
        //创建监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        // 向注册中心进行订阅 override 数据
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        //Ensure that a new exporter instance is returned every time export
        // 创建并返回 DestroyableExporter
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
    }
  2. RegistryProtocol#doLocalExport委托DubboProtocol进行导出

    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
        //dubbo://10.0.75.1:20882/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=com.alibaba.dubbo.demo.DemoService&bind.ip=10.0.75.1&bind.port=20882&dubbo=2.0.2&generic=false&group=group0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=73868&qos.port=22223&revision=1.0.0&side=provider&timestamp=1626098331163&version=1.0.0
        String key = getCacheKey(originInvoker);
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
        if (exporter == null) {
            synchronized (bounds) {
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                if (exporter == null) {
                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                    //默认委托DubboProtocol进行服务暴露
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }
        return exporter;
    }
  3. RegistryProtocol#refer:服务引用

    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        //设置注册中心协议
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        //创建具体注册中心实例
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }
    
        // group="a,b" or group="*" 多分组结果聚合
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                    || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        //处理订阅数据并通过Cluster合并多个Invoker
        return doRefer(cluster, registry, type, url);
    }
  4. RegistryProtocol#doRefer

    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // all attributes of REFER_KEY
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
            URL registeredConsumerUrl = getRegisteredConsumerUrl(subscribeUrl, url);
            //注册消费信息到注册中心
            registry.register(registeredConsumerUrl);
            directory.setRegisteredConsumerUrl(registeredConsumerUrl);
        }
        //订阅服务提供者、路由和动态配置
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
                Constants.PROVIDERS_CATEGORY
                        + "," + Constants.CONFIGURATORS_CATEGORY
                        + "," + Constants.ROUTERS_CATEGORY));
        //通过Cluster合并invokers
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值