Dubbo的服务发布过程

Dubbo架构

整体设计

在这里插入图片描述
首先看下官网说明:

  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry,RegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

依赖关系

在这里插入图片描述

调用链

在这里插入图片描述

Dubbo服务发布流程

在这里插入图片描述

基于dubbo2.7.5分析源码

ServicBean中服务暴露

ServiceBean 这个类,分别实现了 InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener,
BeanNameAware, ApplicationEventPublisherAware

afterPropertiesSet

初始化 bean 的时候会执行该方法 afterPropertiesSet,

这个方法里面会把 dubbo 中配置的 application、registry、service、protocol 等信息,加载到对应的 config实体中,便于后续的使用

onApplicationEvent

spring 容器启动之后,会收到一个这样的事件通知,这里面做了两个事情

  • 判断服务是否已经发布过
  • 如果没有发布,则调用调用 export 进行服务发布的流程(这里就是入口
 public void onApplicationEvent(ContextRefreshedEvent event) {
   
        if (!isExported() && !isUnexported()) {
   
            if (logger.isInfoEnabled()) {
   
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            export(); // 服务暴露入口
        }
    }

export

 public void export() {
   
        super.export();// 重新父类ServiceConfig接口 进行服务发布 
        // 服务发布完之后的事件通知
        publishExportEvent();
    }

ServiceConfig

export

   public synchronized void export() {
   
        // 当前服务是否需要发布,通过配置实现:@Service(export = false)
        if (!shouldExport()) {
   
            return;
        }

        if (bootstrap == null) {
   
            bootstrap = DubboBootstrap.getInstance();
            bootstrap.init();
        }
		// 检查并更新配置信息
        checkAndUpdateSubConfigs();

        //init serviceMetadata
        serviceMetadata.setVersion(version);
        serviceMetadata.setGroup(group);
        serviceMetadata.setDefaultGroup(group);
        serviceMetadata.setServiceType(getInterfaceClass());
        serviceMetadata.setServiceInterfaceName(getInterface());
        serviceMetadata.setTarget(getRef());
        
		// 是否需要延迟发布,通过配置实现:@Service(delay = 1000),单位毫秒
        if (shouldDelay()) {
   
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
   
            // 没有配置delay,直接doExport进行发布
            doExport();
        }
    }

doExport

    protected synchronized void doExport() {
   
    	// 还是在实现发布前的各种判断
        if (unexported) {
   
            throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
        }
        // 服务是否已经发布过了
        if (exported) {
   
            return;
        }
        // 设置发布状态
        exported = true;
        // path 服务路径,默认使用interfaceName,可以@Service(path = "com.helloService")设置
        if (StringUtils.isEmpty(path)) {
   
            path = interfaceName;
        }
        doExportUrls();
        // dispatch a ServiceConfigExportedEvent since 2.7.4
        dispatch(new ServiceConfigExportedEvent(this));
    }

doExportUrls

  • a 加载所有配置的注册中心地址
  • b 遍历所有的配置协议,protocols
  • c 针对每种协议发布一个对应的协议的服务
    private void doExportUrls() {
   
        // applicationModel 用了存储ProvoderModel,发布服务的元数据
        ServiceRepository repository = ApplicationModel.getServiceRepository();
        ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
        repository.registerProvider(
                getUniqueServiceName(),
                ref,
                serviceDescriptor,
                this,
                serviceMetadata
        );
		 // 加载所有配置的注册中心的地址,组装一个URL,以URL驱动
		// registry://192.168.0.4:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=8284&qos.port=22222&registry=zookeeper&release=2.7.5&timestamp=1584713271792
        List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
        for (ProtocolConfig protocolConfig : protocols) {
   
             // group和version组成一个pathKey(serviceName)
            String pathKey = URL.buildKey(getContextPath(protocolConfig)
                    .map(p -> p + "/" + path)
                    .orElse(path), group, version);
            // In case user specified path, register service one more time to map it to path.
            repository.registerService(pathKey, interfaceClass);
            // TODO, uncomment this line once service key is unified
            serviceMetadata.setServiceKey(pathKey);
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

doExportUrlsFor1Protocol

    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
   
        String name = protocolConfig.getName();
        if (StringUtils.isEmpty(name)) {
   
            name = DUBBO;
        }

        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        ServiceConfig.appendRuntimeParameters(map);
        AbstractConfig.appendParameters(map, getMetrics());
        AbstractConfig.appendParameters(map, getApplication());
        AbstractConfig.appendParameters(map, getModule());
        // remove 'default.' prefix for configs from ProviderConfig
        // appendParameters(map, provider, Constants.DEFAULT_KEY);
        AbstractConfig.appendParameters(map, provider);
        AbstractConfig.appendParameters(map, protocolConfig);
        AbstractConfig.appendParameters(map, this);
        /** 用于解析方法级别的配置
 			<dubbo:reference interface="com.xxx.XxxService">
   			 <dubbo:method name="findXxx" timeout="3000" retries="2" />
			</dubbo:reference>
			@Service(methods = {@Method(name = "sayHello",timeout = 1000,retries = 2)
 		*/
            @Method(name = "dasa",timeout = 1000)})
        if (CollectionUtils.isNotEmpty(getMethods())) {
   
            for (MethodConfig method : getMethods()) {
   
                AbstractConfig.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 (CollectionUtils.isNotEmpty(arguments)) {
   
                    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())) {
   
                                                AbstractConfig.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())) {
   
                                                    AbstractConfig.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) {
   
                            AbstractConfig.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(GENERIC_KEY, generic);
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
   
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
   
                map.put(REVISION_KEY, revision);
            }

            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            if (methods.length == 0) {
   
                logger.warn("No method found in service interface " + interfaceClass.getName());
                map.put(METHODS_KEY, ANY_VALUE);
            } else {
   
                map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        // @Service(token="xxx")
        if (!ConfigUtils.isEmpty(token)) {
   
            if (ConfigUtils.isDefault(token)) {
   
                map.put(TOKEN_KEY, UUID.randomUUID().toString());
            } else {
   
                map.put(TOKEN_KEY, token);
            }
        }
        //init serviceMetadata attachments 将解析的参数存在元数据中
        serviceMetadata.getAttachments().putAll(map);

        // 获得当前服务要发布的目标ip和port
        String host = findConfigedHosts(protocolConfig, registryURLs, map);
        Integer port = findConfigedPorts(protocolConfig, name, map);
        // 组装URL
        /* dubbo://192.168.0.4:20880/com.test.dubbo.DemoService?
         anyhost=true&application=demoprovider&bind.ip=192.168.0.4&bind.port=20880
         &deprecated=false&dubbo=2.0.2
        &dynamic=true&generic=false&interface=com.test.dubbo.DemoService
        &methods=sayHello&pid=8188
        &qos.port=22222&release=2.7.5&side=provider&timestamp=1584752528504*/
        URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

        // 外部化配置,这里是通过ConfiguratorFactory去实现动态改变配置的功能
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
   
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }
		// 发布的范围, scope默认null,如果scope不为none,判断是否为local或remote服务,默认两个都会发布
        String scope = url.getParameter(SCOPE_KEY);
        
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值