Dubbo远程方法注册

来源:互联网 发布:usb网络电视接收棒 编辑:程序博客网 时间:2024/06/16 14:11

开始

远程方法暴漏开始的地方是在ServiceBean类,该类实现了ApplicationListener接口,在该接口中接收ontextRefreshEvent事件,通过此事件来开始暴漏对应的dubbo接口。

public void onApplicationEvent(ApplicationEvent event) {        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {            if (isDelay() && ! isExported() && ! isUnexported()) {                if (logger.isInfoEnabled()) {                    logger.info("The service ready on spring started. service: " + getInterface());                }                export();            }        }    }

转换

Dubbo最核心的暴漏方法是将Exportter转为Invoker。是在ServiceConfig doExportUrlsFor1Protocol方法中完成。
具体的核心代码如下:

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

具体的接口

  @Adaptive({Constants.PROXY_KEY})    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

实现类分为三个实现类。我们主要看JavassistProxyFactory类。

 public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {        // TODO Wrapper类不能正确处理带$的类名        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);            }        };    }

获取到Invoker后,在ServiceConfig方法中,继续执行如下:

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

通过对应的接口来获取Exporter。具体接口注释如下:

 /**     * 暴露远程服务:<br>     * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>     * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>     * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>     *      * @param <T> 服务的类型     * @param invoker 服务的执行体     * @return exporter 暴露服务的引用,用于取消暴露     * @throws RpcException 当暴露服务出错时抛出,比如端口已占用     */    @Adaptive    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

我们通过ZK来注册服务,最终进入 RegistryProtocol类中。

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {        //export invoker        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);        //registry provider        final Registry registry = getRegistry(originInvoker);        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);        registry.register(registedProviderUrl);        // 订阅override数据        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为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>() {            public Invoker<T> getInvoker() {                return exporter.getInvoker();            }            public void unexport() {                try {                    exporter.unexport();                } catch (Throwable t) {                    logger.warn(t.getMessage(), t);                }                try {                    registry.unregister(registedProviderUrl);                } catch (Throwable t) {                    logger.warn(t.getMessage(), t);                }                try {                    overrideListeners.remove(overrideSubscribeUrl);                    registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);                } catch (Throwable t) {                    logger.warn(t.getMessage(), t);                }            }        };    }

如下方法具体执行了Invoker和Exporter直接的互转:

private <T> ExporterChangeableWrapper<T>  doLocalExport(final Invoker<T> originInvoker){        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));                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);                    bounds.put(key, exporter);                }            }        }        return (ExporterChangeableWrapper<T>) exporter;    }
 /**     * exporter代理,建立返回的exporter与protocol export出的exporter的对应关系,在override时可以进行关系修改.     *      * @author chao.liuc     *     * @param <T>     */    private class ExporterChangeableWrapper<T> implements Exporter<T>

获取到Exporter后,在获取URL,最后在注册中心注册。

   /**     * 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则,等数据。     *      * 注册需处理契约:<br>     * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。<br>     * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。<br>     * 3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。<br>     * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。<br>     * 5. 允许URI相同但参数不同的URL并存,不能覆盖。<br>     *      * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin     */    void register(URL url);
 @Override    public void register(URL url) {        super.register(url);        failedRegistered.remove(url);        failedUnregistered.remove(url);        try {            // 向服务器端发送注册请求            doRegister(url);        } catch (Exception e) {            Throwable t = e;            // 如果开启了启动时检测,则直接抛出异常            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)                    && url.getParameter(Constants.CHECK_KEY, true)                    && ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());            boolean skipFailback = t instanceof SkipFailbackWrapperException;            if (check || skipFailback) {                if(skipFailback) {                    t = t.getCause();                }                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);            } else {                logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);            }            // 将失败的注册请求记录到失败列表,定时重试            failedRegistered.add(url);        }    }

最终ZK的注册如下:

    protected void doRegister(URL url) {        try {            zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));        } catch (Throwable e) {            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);        }    }

返回新的Exporter:

   //保证每次export都返回一个新的exporter实例        return new Exporter<T>() {            public Invoker<T> getInvoker() {                return exporter.getInvoker();            }            public void unexport() {                try {                    exporter.unexport();                } catch (Throwable t) {                    logger.warn(t.getMessage(), t);                }                try {                    registry.unregister(registedProviderUrl);                } catch (Throwable t) {                    logger.warn(t.getMessage(), t);                }                try {                    overrideListeners.remove(overrideSubscribeUrl);                    registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);                } catch (Throwable t) {                    logger.warn(t.getMessage(), t);                }            }        };

完成注册。

原创粉丝点击