dubbo 服务发布及注册流程

Dubbo 服务发布源码分析

export() 进行服务发布

  1. 首先会获得注册中心集合列表以及协议集合列表,遍历协议中心列表进行多协议发布服务

  2. 服务发布时,会根据配置文件的参数信息封装成map对象,最后将map对象转换成URL地址的参数

  3. 根据URL地址的协议头:registry://xxx ,会在 protocol.exort(url) 中,进入到RegistryProtocol#export()方法中,在该方法中主要做了一下几步

    1. 做本地发布:会根据协议将协议头由 registry:// 转换成 dubbo:// 协议,再次调用Protocol#export进入到DubboProtocol#exprot 中,这里是通过Netty 打开了一个监听
    2. 会根据注册中心的获得一个Registry, 即ZookeeperRegistry 并将服务注册到 zk上。
    3. 最后是发起一个订阅的功能,主要是能动态的去改变服务提供方提供的服务信息。

服务发布及注册流程

ServiceBean 发布(export)入口

org.springframework.context.support.AbstractApplicationContext#refresh -> finishRefresh() -> publishEvent() -> … -> com.alibaba.dubbo.config.spring.ServiceBean#onApplicationEvent()

public class ServiceBean<T> extends ServiceConfig<T> 
	implements InitializingBean, DisposableBean, 
		ApplicationContextAware, ApplicationListener, BeanNameAware {
    // InitializingBean 
    //为接口 bean 提供了初始化方法的方式,凡是实现了该接口的类,在初始化 bean 的时候会执行该方法
    @Override
    public void afterPropertiesSet() throws Exception {
    	//发布入口
        if (!isDelay()) {
        	export();
         }
    }

    // DisposableBean ,bean 被销毁的时候,spring 容器会自动执行 destroy 方法,比如释放资源
    @Override
    public void destroy() throws Exception {}

    // ApplicationContextAware 
    //实现了这个接口的类,当 spring 容器初始化的时候,会自动的将 ApplicationContext 注入进来
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) 
    	throws BeansException {}

    // ApplicationListener ,ApplicationEvent 事件监听,spring 容器启动后会发一个事件通知
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
		//发布入口
        export();
    }

    // BeanNameAware 获得自身初始化时,本身 bean 的 id 属性
    @Override
    public void setBeanName(String s) {}
}

服务发布主要做了什么?

com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol 核心入口开始说起

进行服务发布之前,会获得配置的注册中心集合、以及多协议集合,进行多协议的发布

//发布服务
@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
    //获得注册中心的url地址
    List<URL> registryURLs = loadRegistries(true);
    //根据 配置的多个协议,进行依次发布
    for (ProtocolConfig protocolConfig : protocols) {
        //发布服务根据协议
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}
//发布服务的核心入口
private void doExportUrlsFor1Protocol
	(ProtocolConfig protocolConfig, List<URL> registryURLs) {
	// 执行具体的远程调用
    // registryURL = registry://192.168.1.1.1...
    // proxyFactory = ProxyFactory$Adpative
    // 通过 ProxyFactory 获得 invoker 对象 ,
    // invoker = JavassistProxyFactory$1 代理对象 {url=registry://127.0.0.1..}
	Invoker<?> invoker = proxyFactory.
		getInvoker(ref, (Class) interfaceClass, 
			registryURL.
			addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
    // 服务发布
    // protocol :Protocol$Adpative
    // invoker.url:registry://192.168.1.1.1...
    Exporter<?> exporter = protocol.export(invoker);
    exporters.add(exporter);
}

Protocol$Adpative 类信息

/**
 * @author : guaoran
 * @Description : <br/>
 *  根据 createAdaptiveExtensionClass() 动态获得的 适配器扩展点
 * @date :2019/1/7 13:31
 */
public class Protocol$Adpative implements Protocol {
    //....
    //服务发布
    public Exporter export(Invoker arg0) throws RpcException {
        if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("Invoker argument getUrl() == null");
        //当url=registry://192.168.45.123....时,extension = RegistryProtocol
        URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException
            ("Fail to get extensionProtocol) name from url(" + 
             url.toString() + ") use keys([protocol])");
        // 当url=registry://192.168.45.1...时,
        //  extension = ProtocolListenerWrapper(ProtocolFilterWrapper(RegistryProtocol))
        // 当url=dubbo://192.168.45.1...时,
        //  extension = ProtocolListenerWrapper(ProtocolFilterWrapper(DubboProtocol)) ,
        // todo 为什么 extension 不是 [DubboProtocol] ,
        // todo 因为 在 Protocol 进行loadFile 的时候,会加载 到 clazz.getConstructor(type); 
        //  不报错则 cachedWrapperClasses 不为空,
        // todo 所以在 getExtension("dubbo") 的时候会首先获得 DubboProtocol,
        //  然后会进行封装成 wrapper
        Protocol extension = (Protocol) 
            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

    //客户端引用
    public Invoker refer(Class arg0, URL arg1) throws RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException
            ("Fail to get extension(Protocol) name from url(" + 
             url.toString() + ") use keys([protocol])");
        //此时通过url = registry 时 
        // extension = ProtocolListenerWrapper(ProtocolFilterWrapper(RegistryProtocol))
        Protocol extension = (Protocol) 
            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }
}

继续接着 Exporter<?> exporter = protocol.export(invoker); 走,此时会调用 Protocol$Adpative#export()方法,会通过 getExtensionLoader("registry") 方法获得到 进行Wrapper 包装后的 RegistryProtocol,进入到该类的 RegistryProtocol#export() 方法中。

RegistryProtocol#export() 方法主要做了以下几步:

  1. 做本地发布,在 DubboProtocol 打开一个 Netty 服务进行监听

  2. 获得一个 ZookeeperRegistry 的注册中心的连接,并将服务注册到 zk 上

    /dubbo-demo/com.guaoran.source.dubbo.demo.IHelloService/providers/dubbo%3A%2F%2F19..

  3. 开启一个订阅

    在 zk 上注册一个 /dubbo-demo/com.guaoran.source.dubbo.demo.IHelloService/configurators 节点

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    //export invoker
    // 做本地发布
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
    //registry provider
    // 获得一个注册中心的连接 ZookeeperRegistry
    final Registry registry = getRegistry(originInvoker);
    // 获得需要注册到 zk 上的协议地址,即:dubbo://xxx
    final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
    // 注册到 zk :zkClient.create() , ZookeeperRegistry 的父类
    registry.register(registedProviderUrl);
    // 订阅override数据
    // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,
    // FIXME 因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
    final OverrideListener overrideSubscribeListener = 
        new OverrideListener(overrideSubscribeUrl);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    //保证每次export都返回一个新的exporter实例
    return new Exporter<T>() {
    }

export

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值