注册服务的过程
###加载注册中心链接//加载注册中心,用于链接,dubbo支持多注册中心//注册中心 <dubbo:registry address="zookeeper://127.0.0.1:2181"/>List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);//协议,可以暴露多协议,用,分开//dubbo 协议、端口号 <dubbo:protocol name="dubbo" port="2181"/>ProtocolConfig###服务导出//解析配置Map<String, String> map = new HashMap<String, String>();"side" -> "provider""metadata-type" -> "remote""application" -> "family-provider""release" -> """deprecated" -> "false""dubbo" -> "2.0.2""pid" -> "9812""dynamic" -> "true""interface" -> "org.apache.dubbo.service.EsProductService""generic" -> "false""timestamp" -> "1591705088689"//封装成URL dubbo协议、端口、//dubbo://192.168.56.1:20880/org.apache.dubbo.service.EsProductService?//anyhost=true&application=family-provider&//bind.ip=192.168.56.1&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&//interface=org.apache.dubbo.service.EsProductService&metadata-type=remote&//methods=searchPage&pid=11384&release=&side=provider×tamp=1591706038181URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);//创建invoker//ref服务实例对象、interfaceClass接口、url注册中心:将dubbo服务注册到添加中心//dubbo://192.168.56.1:20880/org.apache.dubbo.service.EsProductService?//anyhost=true&application=family-provider&bind.ip=192.168.56.1&//bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&//interface=org.apache.dubbo.service.EsProductService&metadata-type=remote&//methods=searchPage&pid=5660&release=&side=provider×tamp=1591708521903Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));//建立通信Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);//提供发现能力 RegistryProtocol//通过url 加载 Registery,建立链接 final Registry registry = getRegistry(originInvoker);//将url 注册 到(zk)远程 final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);//创建监听 监听对象://final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);//overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);//向注册中心订阅//registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);public <T> Exporter<T> export(final Invoker<T> originInvoker);//服务暴露 DubboProtocol//将我们的Invoker对象 包装为 DubboExporter 对象//启动服务,创建Netty服务,此时启动了一个IP+Port的Netty服务,可以提供远程服务调用的能力public <T> Exporter<T> export(Invoker<T> invoker);###以上代码只是提供了一个(IP+PORT+Bean+Method)服务,但是这时候消费端是不知道有这么一个服务的
RegistryProtocol
@Overridepublic <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {URL registryUrl = getRegistryUrl(originInvoker);// url to export locallyURL providerUrl = getProviderUrl(originInvoker);// 订阅override数据// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,// 因为subscribed以服务名为缓存的key,导致订阅信息覆盖。final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);// 在本地暴露服务final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);//registry provider// 拿到zookeeper的注册信息final Registry registry = getRegistry(originInvoker);// 获取需要暴露provider的url对象,dubbo的注册订阅通信都是以url作为参数传递的final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);// decide if we need to delay publishboolean register = providerUrl.getParameter(REGISTER_KEY, true);if (register) {//注册服务到注册中心zookerregister(registryUrl, registeredProviderUrl);}// register stated url on provider modelregisterStatedUrl(registryUrl, registeredProviderUrl, register);// 暴露的同时订阅服务,另外会在zk上创建configurators节点信息registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);exporter.setRegisterUrl(registeredProviderUrl);exporter.setSubscribeUrl(overrideSubscribeUrl);notifyExport(exporter);//保证每次export都返回一个新的exporter实例return new DestroyableExporter<>(exporter);}
DubboProtocol
@Overridepublic <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);// 这里会判断是否是stub服务,默认falseBoolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);// 判断是否是callback服务Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);if (isStubSupportEvent && !isCallbackservice) {String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);if (stubServiceMethods == null || stubServiceMethods.length() == 0) {if (logger.isWarnEnabled()) {logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +"], has set stubproxy support event ,but no stub methods founded."));}}}// 开启Netty服务openServer(url);optimizeSerialization(url);return exporter;}
服务暴露将xxxService这个实例对象转换成AbstractProxyInvoker将Invoker对象 包装为 DobboExporter创建Transporter创建Server(NettyServer)服务注册连接Zookeeper创建Provier下的临时节点(绑定服务器节点的URL)监听Zookeeper的configurators节点
RPC原理
为什么要用Dubbo
1.各个应用节点中的url管理维护困难、依赖关系很模糊 -注册中心
2.每个应用节点的性能、并发量、响应时间、没办法评估 -监控中心
protocol 协议
webservice、Thrift、hessain、http、dubbo、rmi
发布服务
dubbo【协议】://ip:20880【端口】/com.xxx.provider.service.xxxService【接口名称】?参数【服务端配置】
URL
dubbo://192.168.56.1:20880/com.city.service.DemoService?anyhost=true&application=hello-world-app&bind.ip=192.168.56.1&bind.port=20880&dubbo=2.6.0&generic=false&interface=com.city.service.DemoService&methods=sayHello&pid=11508&side=provider×tamp=1591614320441
配置参数
发布服务参数介绍dubbo.application.name = 提供方应用信息,用于计算依赖关系dubbo.registry.address = 注册中心,N/A表示不使用注册中心dubbo.protocol.name = 使用的协议dubbo.protocol.port = 端口号dubbo.service.interface = 提供服务的接口名称dubbo.service.ref = 服务的引用实现
zk节点
zk 存放节点路径/dubbo/com.city.common.service.EsProductService/configurators/dubbo/com.city.common.service.EsProductService/providersproviders 下面挂着url临时节点:url持久化节点:/dubbo/com.city.common.service.EsProductService/providers/dubbo/com.city.common.service.EsProductService/configurators
调用服务
基于spring 配置文件拓展NamespaceHander 注册BeanDefinitonParser,利用它来解析BeanDefintionParser 解析配置文件元素spring 默认加载jar 包下/META-INF/spring.handlers 找到对象的NamespaceHanderServiceConfig<T> implementsInitializingBean, 当spring容器初始化完后,会调用afterPropertiesSet方法DisposableBean, 当容器被销毁的时候会调用destroy方法ApplicationContextAware, spring容器初始化以后主动注入ApplicationContext对象BeanNameAware, 获取bean本身id属性ApplicationEventPublisherAware 事件通知

