dubbo源码分析3-consumer订阅创建代理

本文详细分析了Dubbo消费者订阅创建代理的过程,包括RegistryDirectory的创建、消费者订阅信息的注册、服务端列表的订阅、容错层的创建等关键步骤。在创建RegistryDirectory时,保存并解析了订阅信息,通过Cluster.join()方法创建容错层,同时订阅服务提供者信息,以获取服务端列表、配置和路由信息。整个过程涉及到URL参数的合并、订阅信息的处理以及Invoker的创建与缓存。

订阅创建代理的第一部分

红框之前的部分已经分析过,本文分析红框内的部分。

 

 

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {

RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);//保存并解析了订阅信息

directory.setRegistry(registry);

directory.setProtocol(protocol);

// all attributes of REFER_KEY

Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());

URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);

if (!Constants.ANY_VALUE.equals(url.getServiceInterface())

&& url.getParameter(Constants.REGISTER_KEY, true)) {

//注册consumer订阅信息

registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,

Constants.CHECK_KEY, String.valueOf(false)));

}

//订阅并获取 服务端列表,配置,路由信息。然后连接服务端,并创建执行代理的invoker

directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,

 

Constants.PROVIDERS_CATEGORY

+ "," + Constants.CONFIGURATORS_CATEGORY

+ "," + Constants.ROUTERS_CATEGORY));

//创建容错层

Invoker invoker = cluster.join(directory);

ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);

return invoker;

}

注意方法中黑体的部分,本文将围绕 这几个部分逐个分析

  1. 保存并解析了订阅信息。
  2. 注册consumer订阅信息
  3. 订阅并获取 服务端列表,配置,路由信息。然后连接服务端,并创建执行代理的invoker
  4. 创建容错层

 

RegistryDirectory创建分析

执行 RegistryProtocol.doRefer(Cluster, Registry, Class<T>, URL) 时创建,代码如下

RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);

directory.setRegistry(registry);

directory.setProtocol(protocol);

 

public RegistryDirectory(Class<T> serviceType, URL url) {

super(url);//保存url

if (serviceType == null)

throw new IllegalArgumentException("service type is null.");

if (url.getServiceKey() == null || url.getServiceKey().length() == 0)

throw new IllegalArgumentException("registry serviceKey is null.")

this.serviceType = serviceType;

this.serviceKey = url.getServiceKey();

//解析引用参数为map, 解析前的引用参数 url.getParameterAndDecoded(Constants.REFER_KEY): application=young-app&default.timeout=30000000&dubbo=2.6.2&interface=tuling.dubbo.server.UserService&loadbalance=consistenthash&methods=getUser&pid=25060&qos.enable=false&register.ip=169.254.23.140&side=consumer&timeout=30000000&timestamp=1573355812993

this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));

//url.setPath(url.getServiceInterface()).clearParameters()结果: zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService

//addParameters 将引用参数拼接在zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService后面

this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);

String group = directoryUrl.getParameter(Constants.GROUP_KEY, "");

this.multiGroup = group != null && ("*".equals(group) || group.contains(","));//获取是否 配置多组

String methods = queryMap.get(Constants.METHODS_KEY);

this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);//解析方法名数组

}

创建 RegistryDirectory的过程就是保存并解析了订阅信息。

 

RegistryDirectory<T>.mergeUrl(URL) 分析

private URL mergeUrl(URL providerUrl) {

providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // Merge the consumer side parameters 合并 consumer端的参数

 

List<Configurator> localConfigurators = this.configurators; // local reference

if (localConfigurators != null && !localConfigurators.isEmpty()) {// 通过配置 更改providerUrl,需要详细分析。

for (Configurator configurator : localConfigurators) {

providerUrl = configurator.configure(providerUrl);

}

}

//不检查连接是否成功,总是创建invoker

providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false));

 

// The combination of directoryUrl and override is at the end of notify, which can't be handled here

this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); //合并provider 的参数到overrideDirectoryUrl

 

if ((providerUrl.getPath() == null || providerUrl.getPath().length() == 0)// version 1.0 可能出现providerUrl path 为空的情况

&& "dubbo".equals(providerUrl.getProtocol())) { // Compatible version 1.0

//fix by tony.chenl DUBBO-44

String path = directoryUrl.getParameter(Constants.INTERFACE_KEY);

if (path != null) {

int i = path.indexOf('/');

if (i >= 0) {

path = path.substring(i + 1);

}

i = path.lastIndexOf(':');

if (i >= 0) {

path = path.substring(0, i);

}

providerUrl = providerUrl.setPath(path);

}

}

return providerUrl;

}

方法逻辑:合并consumer参数到 provoider 代表的providerUrl

ClusterUtils

将localMap的参数合并到remoteUrl中,将remoteUrl中的 filters and listeners合并到localMap中

public static URL mergeUrl(URL remoteUrl, Map<String, String> localMap) {

Map<String, String> map = new HashMap<String, String>();

Map<String, String> remoteMap = remoteUrl.getParameters();

 

if (remoteMap != null && remoteMap.size() > 0) {

map.putAll(remoteMap);

//一些参数 必须以localMap为准,移除被provider影响的参数

// Remove configurations from provider, some items should be affected by provider.

map.remove(Constants.THREAD_NAME_KEY);

map.remove(Constants.DEFAULT_KEY_PREFIX + Constants.THREAD_NAME_KEY);

 

map.remove(Co

### Dubbo源码分析 #### 负载均衡器选择 Invoker 的详细逻辑 在 Dubbo 框架中,负载均衡器的核心作用是在多个可用的服务提供者之间分配请求流量。具体来说,在负载均衡器选择 `Invoker` 的过程中,Dubbo 首先会获取当前服务的所有可用提供者列表,并通过指定的负载均衡策略来决定最终调用哪个具体的 `Invoker` 实例[^1]。 以下是负载均衡器的主要工作流程: - **获取候选 Provider 列表**:从注册中心拉取所有在线的服务提供方地址。 - **过滤不可用实例**:移除那些因网络异常或其他原因而无法正常工作的服务实例。 - **执行负载均衡算法**:根据配置的负载均衡策略(如随机、轮询或一致性哈希),计算并返回目标 `Invoker`。 #### 配置管理机制解析 Dubbo 支持多种方式加载配置信息,其中一种常见的方式是通过 ZooKeeper 作为配置中心。为了确保系统的高可靠性和灵活性,Dubbo 提供了一套完善的配置优先级规则: 1. **本地配置覆盖远程配置**:如果同一参数既定义在本地也存在远端,则以本地为准[^2]。 2. **默认值补充缺失字段**:当某些必要选项未被显式声明时,默认设置会被自动填充进去。 这种设计不仅简化了运维操作难度,还增强了应对突发状况的能力——即使连接不上外部存储库也能维持基本功能运转。 #### 请求处理链路剖析 当客户端发起一次 RPC 调用后,该请求经过一系列处理器层层传递直至抵达实际业务方法之前经历了复杂的转换过程。以下列举几个关键环节及其职责所在: - **解码阶段 (DecodeHandler)** :负责将字节流还原成 Java 对象形式以便后续步骤能够理解其含义; - **头部交换协议适配层(HeaderExchangeHandler)** :主要完成消息头部分的数据组装拆分任务; - **核心协议实现类(DubboProtocol.requestHandler)** :承担着最为繁重的工作量,包括但不限于序列化反序列化控制以及线程池调度安排等等[^3]. 以上各组件紧密协作共同构建起了完整的通信桥梁结构图谱如下所示: ```mermaid sequenceDiagram participant Client as 客户端 participant NettyServer as Netty服务器 participant DecodeHandler as 解码处理器 participant HeaderExchangeHandler as 头部交换处理器 participant RequestHandler as 请求处理器 Client->>NettyServer: 发送数据包 activate NettyServer Note over NettyServer: 接收到原始二进制数据\n触发回调函数 NettyServer->>DecodeHandler: 执行decode()方法 activate DecodeHandler DecodeHandler-->>NettyServer: 返回Java对象表示的消息体 deactivate DecodeHandler NettyServer->>HeaderExchangeHandler: 继续向下游转发已解析好的Request实体 activate HeaderExchangeHandler HeaderExchangeHandler->>RequestHandler: 委托给requestHandler进一步加工 activate RequestHandler ... 更深层次的具体事务逻辑 ... end ``` #### 动态上下线通知机制探讨 对于动态调整集群规模场景下的优雅停机需求而言,Dubbo 设计了一个简单有效的解决方案即主动注销机制。每当某个 Provider 准备退出运行环境前都会提前告知 Registry 自己即将消失的事实;随后 Consumer订阅到这一变更事件之后便会及时更新内部缓存状态从而避免继续尝试访问已经失效的目标资源[^4]。 --- ### 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值