Dubbo 源码阅读(八)RegistryProtocol

RPC过程

在这里插入图片描述

源码

@Override
@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
											//"registry"           "dubbo"
    url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
    //根据url取得注册中心                   见8.1
    Registry registry = registryFactory.getRegistry(url);
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // group="a,b" or group="*"
    Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
    String group = qs.get(Constants.GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                || "*".equals(group)) {
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    		//见8.2
    return doRefer(cluster, registry, type, url);
}

8.1 getRegistry

//默认用别名为dubbo的实现
@SPI("dubbo")
public interface RegistryFactory {
	//可以在url里添加protocol=xx来更改协议
    @Adaptive({"protocol"})
    Registry getRegistry(URL url);
}

8.2 doRefer

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
	//每一个服务私有一个RegistryDirectory,缓存Map<url,invoker>,同时也是监听器
    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)) {
        registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                Constants.CHECK_KEY, String.valueOf(false)));
    }
    //注册监听节点,即向注册中心订阅服务
    directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
            Constants.PROVIDERS_CATEGORY
                    + "," + Constants.CONFIGURATORS_CATEGORY
                    + "," + Constants.ROUTERS_CATEGORY));
	//Invoker 让一切对象调用,统一成一个入口
	//cluster 容错:一台机器出错,调另一台机器
    Invoker invoker = cluster.join(directory);
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}

在这里插入图片描述
跟 join:

@SPI(FailoverCluster.NAME)
public interface Cluster {
	//可以在@Reference(cluster="")里配置
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;
}

根据@SPI选择实现类

public class FailoverCluster implements Cluster {

    public final static String NAME = "failover";

    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<T>(directory);
    }

}

FailoverClusterInvoker 里面有个 doInvoke 方法,在它的父类的 invoke 里被调用

 @Override
@SuppressWarnings({"unchecked", "rawtypes"})
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    List<Invoker<T>> copyinvokers = invokers;
    checkInvokers(copyinvokers, invocation);
    //容错次数
    int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
    if (len <= 0) {
        len = 1;
    }
    // retry loop.
    RpcException le = null; // last exception.
    List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
    Set<String> providers = new HashSet<String>(len);
    for (int i = 0; i < len; i++) {
        //Reselect before retry to avoid a change of candidate `invokers`.
        //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
        if (i > 0) {
            checkWhetherDestroyed();
            copyinvokers = list(invocation);
            // check again
            checkInvokers(copyinvokers, invocation);
        }
        Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
        invoked.add(invoker);
        RpcContext.getContext().setInvokers((List) invoked);
        try {
            Result result = invoker.invoke(invocation);
            if (le != null && logger.isWarnEnabled()) {
                logger.warn("Although retry the method " + invocation.getMethodName()
                        + " in the service " + getInterface().getName()
                        + " was successful by the provider " + invoker.getUrl().getAddress()
                        + ", but there have been failed providers " + providers
                        + " (" + providers.size() + "/" + copyinvokers.size()
                        + ") from the registry " + directory.getUrl().getAddress()
                        + " on the consumer " + NetUtils.getLocalHost()
                        + " using the dubbo version " + Version.getVersion() + ". Last error is: "
                        + le.getMessage(), le);
            }
            //当前调用成功就返回
            return result;
        } catch (RpcException e) {
            if (e.isBiz()) { // biz exception.
                throw e;
            }
            le = e;
        } catch (Throwable e) {
            le = new RpcException(e.getMessage(), e);
        } finally {
            providers.add(invoker.getUrl().getAddress());
        }
    }
    throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method "
            + invocation.getMethodName() + " in the service " + getInterface().getName()
            + ". Tried " + len + " times of the providers " + providers
            + " (" + providers.size() + "/" + copyinvokers.size()
            + ") from the registry " + directory.getUrl().getAddress()
            + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
            + Version.getVersion() + ". Last error is: "
            + (le != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
}

在这里插入图片描述

<think>我们正在处理一个关于Dubbo源码主流程解析的请求。用户希望了解Dubbo源码的主流程,特别是服务提供者启动和服务导出的过程。 根据引用,我们可以总结出以下关键点: 1. Dubbo的核心模块包括服务导出、注册中心、协议支持等(引用[1])。 2. 服务提供者启动时,会涉及到配置解析、服务导出、注册中心注册和启动服务等步骤(引用[2])。 3. 服务导出的基本原理包括:确定服务参数、确定协议、构造URL、注册到注册中心、启动服务(引用[3])。 4. Dubbo使用了SPI扩展机制,通过动态生成代理类来实现扩展(引用[4][5])。 因此,我们将重点解析服务提供者启动的主流程,包括: - 配置解析:解析XML配置或注解配置,生成服务配置对象。 - 服务导出:将服务配置转化为可执行的服务,包括生成Invoker、Exporter,并注册到注册中心。 - 启动服务:根据协议启动服务器(如NettyServer)监听请求。 我们将按照以下步骤详细说明: 1. 配置解析:Dubbo通过Spring自定义标签解析或注解驱动来解析服务配置。 2. 服务导出:当Spring容器刷新时,Dubbo会触发服务导出。主要步骤包括: a. 检查配置,组装URL。 b. 创建Invoker对象(代表服务的可执行对象)。 c. 通过协议导出服务,生成Exporter(管理服务生命周期)。 d. 将服务注册到注册中心(如Zookeeper)。 3. 启动服务:在协议导出过程中,会启动服务器(如NettyServer)监听端口。 下面我们将详细展开这些步骤。</think>### Dubbo源码主流程解析 Dubbo的核心流程围绕**服务导出**(服务提供者)和**服务引入**(消费者)展开,主流程如下: #### **1. 配置解析与初始化** - **XML/注解解析**:Dubbo通过Spring自定义标签或注解解析配置(如`<dubbo:service>`),生成`ServiceBean`对象[^3][^4]。 - **组装URL**:将配置参数(接口名、协议、注册中心地址等)封装为**URL对象**(Dubbo的统一配置模型)[^3][^5]。 - 示例URL:`dubbo://192.168.1.1:20880/com.example.UserService?timeout=1000` #### **2. 服务导出(服务提供者)** - **创建Invoker**: - 将服务实现类包装为`AbstractProxyInvoker`(基于Javassist或JDK动态代理生成)[^5]。 - 实现远程调用逻辑:将方法调用转化为网络请求[^3][^4]。 - **协议导出**: - 通过`Protocol.export()`方法(默认`DubboProtocol`)启动网络服务(如Netty Server)[^3][^5]。 - 关键步骤: ```java Exporter<?> exporter = protocol.export(wrapperInvoker); // 导出服务 ``` - **注册到注册中心**: - 将服务URL注册到ZooKeeper等注册中心(通过`RegistryProtocol`)[^3]。 - 消费者通过注册中心发现服务提供者地址。 #### **3. 服务引入(消费者)** - **订阅服务**: - 消费者从注册中心获取提供者URL列表[^3][^4]。 - **创建代理对象**: - 通过`ProxyFactory`(默认`JavassistProxyFactory`)生成服务接口的代理类[^5]。 - 调用方法时,代理类将请求转发给集群模块(Cluster)。 #### **4. 远程调用** - **集群容错**: - `Cluster`模块处理负载均衡(如Random)、容错(如Failover)[^4]。 - **网络通信**: - 通过`ExchangeClient`(默认Netty实现)发送请求[^3][^5]。 - 提供者收到请求后,由`DubboProtocol.requestHandler`执行本地调用并返回结果[^3]。 #### **5. 关键设计** - **SPI扩展机制**: - 核心接口(`Protocol`、`Cluster`等)通过SPI动态加载实现类,支持扩展(如替换为REST协议)[^4][^5]。 - **URL总线**: - 所有配置和状态通过URL传递,贯穿整个流程[^3][^5]。 #### **流程图** ```mermaid graph LR A[服务提供者] --> B[解析配置生成URL] B --> C[创建Invoker] C --> D[Protocol.export 启动服务] D --> E[注册中心注册URL] F[消费者] --> G[订阅服务获取URL] G --> H[创建代理对象] H --> I[调用代理方法] I --> J[Cluster选择提供者] J --> K[Netty发送请求] K --> A ``` ### 总结 Dubbo主流程围绕**URL配置模型**和**SPI扩展机制**,通过**服务导出/引入**实现远程通信。核心步骤包括: 1. 配置解析 → 2. 服务导出(启动服务+注册中心) → 3. 服务引入(订阅+生成代理) → 4. 远程调用(集群容错+网络通信)[^3][^4][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值