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); } } };
完成注册。
阅读全文
0 0
- Dubbo远程方法注册
- Dubbo远程方法注册
- Dubbo无法访问远程Zookeeper已注册服务的问题
- Dubbo学习笔记:注册到zookeeper并实现远程调用
- 利用ASP远程注册DLL的方法
- 利用ASP远程注册DLL的方法
- 利用ASP远程注册DLL的方法
- dubbo容器-注册中心
- Dubbo-注册中心
- Dubbo注册中心
- Dubbo多注册中心
- Dubbo注册中心
- Docker dubbo 服务注册
- Dubbo注册中心介绍
- Dubbo注册中心介绍
- dubbo 服务注册
- Dubbo的注册中心
- dubbo注册中心
- 王坚博士:挑战离年轻人更近,未来才离我们更近
- indexOf的运用:查找字符串是否存在
- TortoiseSVN下载,安装,配置,常用操作 svn教程
- golang中管道替换问题
- mysql 批量插入数据(INNODB)优化
- Dubbo远程方法注册
- soket实现远程执行系统命令
- long类型转换成日期
- jQuery中关于jQuery.fn.init.prototype = jQuery.fn的解读
- 使用SQLyog将Excel数据导入Mysql数据库
- 日志管理
- <button>和<input type="button"> 的区别
- PHP取数组中几个数值求和等于指定值的算法
- 实施基础知识总结