Dubbo本地和远程服务暴露原理

一、前言

当dubbo的类交于Spring容器管理后,此时初步的初始化已经完成,接下来就要开始服务的暴露;
服务暴露的入口方法是ServiceBean的onApplicationEvent;

二、执行流程

ServiceBean

 @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
     //是不是已经暴露或 是不是被取消 是不是被延迟
        if (isDelay() && !isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            //调用父类ServiceConfig的export()
            export();
        }
    }

ServiceConfig

public synchronized void export() {
     //刚开始provider时空的
        if (provider != null) {
            if (export == null) {
                export = provider.getExport();
            }
            if (delay == null) {
                delay = provider.getDelay();
            }
        }
      //表示服务是否已经暴露
        if (export != null && !export) {
            return;
        }
      //是否设置了延迟暴露
        if (delay != null && delay > 0) {
            delayExportExecutor.schedule(new Runnable() {
                @Override
                public void run() {
                    doExport();
                }
            }, delay, TimeUnit.MILLISECONDS);
        } else {
            doExport();
        }
    }
    
protected synchronized void doExport() {
//如果取消暴露,则直接抛出异常
if (unexported) {
    throw new IllegalStateException("Already unexported!");
}
//如果已经暴露过,则返回
if (exported) {
    return;
}
exported = true;
if (interfaceName == null || interfaceName.length() == 0) {
    throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
//如果ProviderConfig为空,则为它进行赋值
checkDefault();
if (provider != null) {
    if (application == null) {
        application = provider.getApplication();
    }
    if (module == null) {
        module = provider.getModule();
    }
    if (registries == null) {
        registries = provider.getRegistries();
    }
    if (monitor == null) {
        monitor = provider.getMonitor();
    }
    if (protocols == null) {
        protocols = provider.getProtocols();
    }
}
if (module != null) {
    if (registries == null) {
        registries = module.getRegistries();
    }
    if (monitor == null) {
        monitor = module.getMonitor();
    }
}
if (application != null) {
    if (registries == null) {
        registries = application.getRegistries();
    }
    if (monitor == null) {
        monitor = application.getMonitor();
    }
}
if (ref instanceof GenericService) {
    interfaceClass = GenericService.class;
    if (StringUtils.isEmpty(generic)) {
        generic = Boolean.TRUE.toString();
    }
} else {
    try {
        interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                .getContextClassLoader());
    } catch (ClassNotFoundException e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
    // 检查接口和方法,接口不能为 null 并且必须是 interface,方法必须属于接口
    checkInterfaceAndMethods(interfaceClass, methods);
    // 检查 ref 配置,不能为 null,并且必须是配置的接口的实现类
    checkRef();
    generic = Boolean.FALSE.toString();
}
if (local != null) {
    if ("true".equals(local)) {
        local = interfaceName + "Local";
    }
    Class<?> localClass;
    try {
        localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
    } catch (ClassNotFoundException e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
    if (!interfaceClass.isAssignableFrom(localClass)) {
        throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
    }
}
if (stub != null) {
    if ("true".equals(stub)) {
        stub = interfaceName + "Stub";
    }
    Class<?> stubClass;
    try {
        stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
    } catch (ClassNotFoundException e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
    if (!interfaceClass.isAssignableFrom(stubClass)) {
        throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
    }
}
/* 检查 application 配置 */
checkApplication();
/* 检查 registry 配置 */
checkRegistry();
/* 检查 protocol 配置 */
checkProtocol();
/* 追加 properties 配置 */
appendProperties(this);
/* 检查 stub、mock 配置 */
checkStubAndMock(interfaceClass);
if (path == null || path.length() == 0) {
    path = interfaceName;
}
 //上面大多是检测配置属性和映射,之前调用此方法暴露服务的url;
doExportUrls();    
// 构造 ProviderModel,将服务的方法封装成 ProviderMethodModel 保存到集合中,并将集合与方法名称做映射保存
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
// 获取服务唯一名称,注册到已提供服务列表中
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel); 
}


 private void doExportUrls() {
        //loadRegistries(true)是AbstractInterfaceConfig的方法
        //此方法主要是获取注册中心的url
        //registry://192.168.192.1:20880/hhh.service.DemoService?application=untitled&dubbo=2.6.2&pid=15960&registry=zookeeper&timestamp=1608196602048
        List<URL> registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols) {
        //遍历协议配置,并注册
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
  
  
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        String name = protocolConfig.getName();
        //如果协议名为空,则默认设置为dubbo
        if (name == null || name.length() == 0) {
            name = "dubbo";
        }
           //把跟要暴露服务相关属性和服务接口的方法名放入map中;(下面会有map的截图)            
        Map<String, String> map = new HashMap<String, String>();
        map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        if (ConfigUtils.getPid() > 0) {
            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
        }
        //将对象字段通过反射添加到map中
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, provider, Constants.DEFAULT_KEY);
        appendParameters(map, protocolConfig);
        appendParameters(map, this);
        if (methods != null && !methods.isEmpty()) {
            for (MethodConfig method : methods) {
                appendParameters(map, method, method.getName());
                String retryKey = method.getName() + ".retry";
                if (map.containsKey(retryKey)) {
                    String retryValue = map.remove(retryKey);
                    if ("false".equals(retryValue)) {
                        map.put(method.getName() + ".retries", "0");
                    }
                }
                List<ArgumentConfig> arguments = method.getArguments();
                if (arguments != null && !arguments.isEmpty()) {
                    for (ArgumentConfig argument : arguments) {
                        // convert argument type
                        if (argument.getType() != null && argument.getType().length() > 0) {
                            Method[] methods = interfaceClass.getMethods();
                            // visit all methods
                            if (methods != null && methods.length > 0) {
                                for (int i = 0; i < methods.length; i++) {
                                    String methodName = methods[i].getName();
                                    // target the method, and get its signature
                                    if (methodName.equals(method.getName())) {
                                        Class<?>[] argtypes = methods[i].getParameterTypes();
                                        // one callback in the method
                                        if (argument.getIndex() != -1) {
                                            if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                                appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                            } else {
                                                throw new IllegalArgumentException("argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        } else {
                                            // multiple callbacks in the method
                                            for (int j = 0; j < argtypes.length; j++) {
                                                Class<?> argclazz = argtypes[j];
                                                if (argclazz.getName().equals(argument.getType())) {
                                                    appendParameters(map, argument, method.getName() + "." + j);
                                                    if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                        throw new IllegalArgumentException("argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        } else if (argument.getIndex() != -1) {
                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                        } else {
                            throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                        }

                    }
                }
            } // end of methods for
        }

        if (ProtocolUtils.isGeneric(generic)) {
            map.put(Constants.GENERIC_KEY, generic);
            map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
        } else {
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);
            }

            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            if (methods.length == 0) {
                logger.warn("NO method found in service interface " + interfaceClass.getName());
                map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
            } else {
                map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        if (!ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
            } else {
                map.put(Constants.TOKEN_KEY, token);
            }
        }
        if (Constants.LOCAL_PROTOCOL.equals(protocolConfig.getName())) {
            protocolConfig.setRegister(false);
            map.put("notify", "false");
        }
        // export service
        String contextPath = protocolConfig.getContextpath();
        if ((contextPath == null || contextPath.length() == 0) && provider != null) {
            contextPath = provider.getContextpath();
        }
              
        String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
        Integer port = this.findConfigedPorts(protocolConfig, name, map);
        //组装成url,可见下面的图
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }
         //以下是导出服务的逻辑
         //根据scope决定导出逻辑
         //1.scope=none,不导出服务
         //2.scope!=remote,导出到本地
         //3.scope!=local,导出到远程
        String scope = url.getParameter(Constants.SCOPE_KEY);
        // don't export when none is configured
        if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
                   
            // export to local if the config is not remote (export to remote only when config is remote)
        
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
            //导出到本地,见1.1详解
                exportLocal(url);
            }
            //剩余代码是导出到远程,见1.2详解
 );
               
        

map集合
在这里插入图片描述
url
在这里插入图片描述

1.1本地暴露详解;

ServiceConfig

 private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
     // 拼装本地暴露 URL,指定为 injvm 协议,这个协议不会打开端口而是把服务保存在内存中
     //URL local可见下面的图
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(LOCALHOST)
                    .setPort(0);
                    //把接口的实现类加入serviceClassHolder中
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
            //此处用了两次适应扩展,见编号1.1.1和1.1.2
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
  

URL local
在这里插入图片描述

1.1.1proxyFactory

@SPI("javassist")
public interface ProxyFactory {

   
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

 //根据SPI和@Adaptive属性生成动态代理类JavassistProxyFactory,执行里面的getInvoker方法
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}

JavassistProxyFactory

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };

1.1.2protocol

@SPI("dubbo")
public interface Protocol {

    int getDefaultPort();

//执行的是DubboProtocol的export方法
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

思考:为什么要本地暴露服务
可能存在服务提供方和服务消费方在同一个JVM的情况,将服务提供方暴露在本地JVM下可以让服务消费方直接调用,这样避免了远程调用的网络开销;

1.2远程暴露详解;

ServiceConfig


 if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
        if (registryURLs != null && !registryURLs.isEmpty()) {
                //有注册中心的,向注册中心注册并暴露;
                    for (URL registryURL : registryURLs) {
                //组装成的url见下面的图;
                        url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                        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()));
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                //此处是DubboProtocol的export方法;注册RegistryProtocol
                        Exporter<?> exporter = 
                        protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
                // 没有注册中心,直连;
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                //此方法是DubboProtocol的export方法;
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            }
        }
        this.urls.add(url);

url
在这里插入图片描述

DubboProtocol

public class DubboProtocol extends AbstractProtocol {
@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        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;
    }
    
private void openServer(URL url) {
        // find server.
        //获取ip;
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
        if (isServer) {
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
            //把ip地址加入map中;
                serverMap.put(key, createServer(url));
            } else {
                // server supports reset, use together with override
                server.reset(url);
            }
        }
        
private ExchangeServer createServer(URL url) {
        // send readonly event when server closes, it's enabled by default
        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
        // enable heartbeat by default
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);

        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);

        url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
        ExchangeServer server;
        try {         
        //通过Transporter,让server与交换机进行连接;
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
        str = url.getParameter(Constants.CLIENT_KEY);
        if (str != null && str.length() > 0) {
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            if (!supportedTypes.contains(str)) {
                throw new RpcException("Unsupported client type: " + str);
            }
        }
        return server;

AbstractServer

 public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();

        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
            bindIp = NetUtils.ANYHOST;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
        try {
        //NettyServer类的doOpen(),主要是bindip和port,可以理解为建立通信;
            doOpen();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                    + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        //fixme replace this with better method
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    }

1.3注册中心的执行原理;

RegistryProtocol

 @Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //export invoker
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
       //获取注册中心URL; zookeeper://192.168.192.1.20880/hhh.service....
        URL registryUrl = getRegistryUrl(originInvoker);
       //获取提供者的URL; dubbo://192.168.192.1.20880/hhh.service...
        //registry provider
        final Registry registry = getRegistry(originInvoker);
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);

        //to judge to delay publish whether or not
        boolean register = registedProviderUrl.getParameter("register", true);
       //将提供者信息注册到服务提供者与消费者的注册表中
        ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);

        if (register) {
        //向注册中心注册,最底层是调用ZookeeperRegistry的doRegister方法;
            register(registryUrl, registedProviderUrl);
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        //Ensure that a new exporter instance is returned every time export
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
    }

三、总结

dubbo的服务暴露可分为三部分;

1、检测配置是否为空,为空则使用默认,并根据配置信息组装成URL;
2、暴露服务,包括本地和远程;
3、注册到注册中心;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值