dubbo学习二-dubbo原理

目录

 

1 RPC调用过程

2 netty通信框架简介

3 dubbo原理

3.1 框架设计

3.2 服务暴露流程

3.3 服务引用流程

3.4 服务调用过程


1 RPC调用过程

1. 调用者(客户端Client)以本地调用的方式发起调用;
2. Client stub(客户端存根)收到调用后,负责将被调用的方法名、参数等打包编码成特定格式的能进行网络传输的消息体;
3. Client stub找到服务地址,并将消息体发送给服务端;
4. Server stub(服务端存根)收到通过网络接收到消息后按照相应格式进行拆包解码,获取方法名和参数;
5. Server stub解码结果进行本地调用;
6. 被调用者(Server)本地调用执行后将结果返回给server stub;
7. Server stub将返回值打包编码成消息,并通过网络发送给客户端;
8. Client stub收到消息后,进行拆包解码,返回给Client;
9. Client得到本次RPC调用的最终结果。

2 netty通信框架简介

服务提供者和调用者通过nio框架Netty进行通信。NIO是非阻塞IO,和传统IO有很大区别
传统IO模型如下


BIO服务器给每个请求分配一个线程来处理请求。这样的话每个服务器不可能处理太多请求。

NIO服务器
每个请求通过Channel(通道)来发送数据,通过Buffer(缓冲区)进行传输,这些通道注册到Selector(多路复用器)上,某个通道的任意一个状态好了,可以单开一个线程来处理。


通道状态包括Connect(连接就绪),Accept(接收就绪),Read(读就绪),Write(写就绪)

Netty基本模型
Netty对NIO做的进一步封装,是一个高性能NIO框架。


1. Netty服务器启动,监听一个端口,如dubbo框架,监听20880,初始化一个通道(NioServerSocketChannel)
2. 把NioServerSocketChannel注册到Selector上,轮询accept事件,即通道准备接收数据的事件
3. accept事件触发后处理通道的信息,建立起与客户端连接的Channel(NioSocketChannel)
4. 把NioSocketChannel注册到另一个Selector上,这个Selector轮询read和write事件
5. 读就绪或写就绪后就会把任务抛给任务队列执行

3 dubbo原理

3.1 框架设计

dubbo框架整体分为3层,业务层、RPC层、远程通信层

  • 业务层

    对于开发者来说只是使用到业务层,写一个接口和一个实现,想要远程调用只需要调接口方法就行了

  • RPC层

    RPC层分为Config层、Proxy层、Registry层、Cluster层、Monitor层、Protocol层
    Config:配置层,封装配置文件配置的信息
    Proxy:服务代理层,通过代理层生成客户端、服务端代理对象,通过代理对象来完成调用
    Registry:注册中心层,服务提供者需要注册到注册中心,调用者需要从注册中心订阅需要的服务
    Cluster:路由层,有可能一个服务在多台机器上都有,这一层可以做到负载均衡
    Monitor:监控中心层,每一次调用信息都会发给监控层,监控层收到数据就会在界面上展示监控数据
    Protocol:远程调用层,封装整个RPC调用。

  • 远程通信层

    Exchange:信息交换层,创建一个客户端,一个服务端,两者架起管道进行数据传输
    Transport:传输层,真正传输数据的一层,Transport底层就是Netty框架
    Serial:序列化层,整个传输过程中发送数据需要序列化,接收数据需要进行反序列化

3.2 服务暴露流程

ServiceBean实现了ApplicationListener接口的onApplicationEvent方法,spring容器启动完后会执行服务暴露方法

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

private void doExportUrls() {
    // 获取注册中心地址
    List<URL> registryURLs = this.loadRegistries(true);
    Iterator i$ = this.protocols.iterator();

    // 把服务暴露在协议端口。可以配置多个暴露端口
    while(i$.hasNext()) {
        ProtocolConfig protocolConfig = (ProtocolConfig)i$.next();
        this.doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }

}

doExportUrlsFor1Protocol方法关键代码:

// 获取执行器
Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));
// 进一步包装
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 暴露invoker
Exporter<?> exporter = protocol.export(wrapperInvoker);
this.exporters.add(exporter);

可以看到,invoker实际上就是一个代理对象,封装了我们写的Service实现类

protocol.export过程:通过RegistryProtocol.export()暴露
先通过然后通过DubboProtocol暴露
DubboProtocol.export重要代码:

// 启动Netty服务器,监听20880
this.openServer(url);
this.optimizeSerialization(url);

然后通过ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);注册服务提供者

public static void registerProvider(Invoker invoker, URL registryUrl, URL providerUrl) {
    // 封装invoker
    ProviderInvokerWrapper wrapperInvoker = new ProviderInvokerWrapper(invoker, registryUrl, providerUrl);
    // 获取服务名(service接口全类名:版本)
    String serviceUniqueName = providerUrl.getServiceKey();
    Set<ProviderInvokerWrapper> invokers = (Set)providerInvokers.get(serviceUniqueName);
    if (invokers == null) {
        providerInvokers.putIfAbsent(serviceUniqueName, new ConcurrentHashSet());
        invokers = (Set)providerInvokers.get(serviceUniqueName);
    }

    // 把invoker放到内存
    invokers.add(wrapperInvoker);
}

至此,服务暴露完成,梳理下服务暴露过程如下:
1 Spring容器启动完成,触发onApplicationEvent方法,执行doExport方法开始暴露服务
2 获取注册中心地址,把服务暴露在协议端口
3 获取执行器,然后进一步包装,通过RegistryProtocol.export暴露包装后的执行器
4 通过DubboProtocol启动netty服务器
5 通过ProviderConsumerRegTable注册服务提供者

3.3 服务引用流程

从ReferenceBean说起,这个类实现了FactoryBean类的getObject方法,通过工厂方式获取一个个引用的服务

public Object getObject() throws Exception {
    return this.get();
}
public synchronized T get() {
    if (this.destroyed) {
        throw new IllegalStateException("Already destroyed!");
    } else {
        if (this.ref == null) {
            this.init();
        }

        return this.ref;
    }
}

看一下init重要代码
// 创建代理对象,map里存放注册中心地址、调用的方法、调用的接口、版本等
this.ref = this.createProxy(map);
createProxy方法:

if (this.urls.size() == 1) {
    // 远程引用调用的接口
    this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
} else {

通过RegistryProtocol.refer()引用
doRefer方法:

// 订阅服务
directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
// 进入Dubbo.refer方法,连接netty服务器,获取invoker,封装的有注册中心地址,dubbo协议地址
Invoker invoker = cluster.join(directory);
// 把invoker注册到内存
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
public static void registerConsumer(Invoker invoker, URL registryUrl, URL consumerUrl, RegistryDirectory registryDirectory) {
    ConsumerInvokerWrapper wrapperInvoker = new ConsumerInvokerWrapper(invoker, registryUrl, consumerUrl, registryDirectory);
    String serviceUniqueName = consumerUrl.getServiceKey();
    Set<ConsumerInvokerWrapper> invokers = (Set)consumerInvokers.get(serviceUniqueName);
    if (invokers == null) {
        consumerInvokers.putIfAbsent(serviceUniqueName, new ConcurrentHashSet());
        invokers = (Set)consumerInvokers.get(serviceUniqueName);
    }

    invokers.add(wrapperInvoker);
}

服务引用流程过程:
1 ReferenceBean通过工厂模式调用getObject,然后创建代理对象
2 通过protocol引用远程服务
3 通过DubboProtocol和远程netty服务器建立连接,创建客户端
4 通过RegistryProtocol从注册中心订阅服务,把创建的invoker信息注册到注册表中
5 返回invoker代理对象

3.4 服务调用过程


1 代理对象把请求方法、请求参数进行封装
2 如果有Filter,执行Filter相关处理
3 执行ClusterInvoker,如果封装了多个Invoker会通过负载均衡策略选出一个invoker来执行
4 调用其他filter进行调用信息统计
5 协议invoker进行调用(DubboInvoker、RmiInvoker、HessianInvoker等)
6 Client通过Netty客户端连接到服务端发送请求

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值