Dubbo 服务端源码深入分析 (7)

目录

1. 前提

2. 认识 Protocol 和 ProxyFactory

Protocal

ProxyFactory

Dubbo服务流程

服务端源码分析

测试代码:

Protocal代理的源码

ProxyFactory源码:

获取invoker对象

具体步骤

1. 我们调用的是ProxyFactory的代理对象的getInvoker方法

2. 我们把断点打到 JavassistProxyFactory 类的对应方法处,进入断点并且封装了一个invoker对象进行返回

服务暴露:

1. 我们调用的是Portocal对象的静态代理类的export方法

 2. 直接把断点打到 RegistryProtocal类的export方法上

3. 重点分析服务暴露doLocalExport方法


1. 前提

本来想着一篇文章分析完dubbo主流程源码的,但是写完Dubbo 基于xml文件分析主流程源码 (4)_chen_yao_kerr的博客-CSDN博客以后发现,源码中存在大量的Dubbo SPI思想,不说清楚Dubbo SPI, 源码是看不下去的。因此,紧接着我又补了2篇Dubbo SPI的博客。

所以有了前3篇博客的铺垫,再次来谈源码,相信会简单很多。

2. 认识 Protocol 和 ProxyFactory

不了解这两个接口,今天的源码分析是走不下去的,它是Dubbo SPI思想运用于Dubbo框架非常重要的实践。

Protocal

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> var1) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;

    void destroy();
}

他有很多实现类:

 而这些子类的key-value形式,都是配置在如下路径中:

 先对这些概率有个大概的了解,后面会在代码里面直接提到这个key,希望对照这些图片,能够快速的知道我在说的是什么类。

ProxyFactory


package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;

@SPI("javassist")
public interface ProxyFactory {
    @Adaptive({"proxy"})
    <T> T getProxy(Invoker<T> var1) throws RpcException;

    @Adaptive({"proxy"})
    <T> T getProxy(Invoker<T> var1, boolean var2) throws RpcException;

    @Adaptive({"proxy"})
    <T> Invoker<T> getInvoker(T var1, Class<T> var2, URL var3) throws RpcException;
}

它和上面的接口一样,也有很多的子类:

 而这些子类的key-value形式,都是配置在如下路径中:

Dubbo服务流程

1. 需要区分2个URL,第一个是连接注册中心的URL, 注册中心可能是nacos、euraka等等。我使用的是zookeeper。  第二个URL是服务提供方的URL,也就是实际提供服务的机器。注册中心只是一个管理服务提供方的中间件,在注册中心中,我们可以找到这些真正提供服务的机器的ip、应用名、暴露的服务接口、方法等等信息。

2. 我们需要把想要暴露的服务接口的实现类实例化,经过各种封装、最终成为invoker对象,而invoker对象含有 ip、端口号、接口、方法、应用名等信息

3. 通过协议把invoker给暴露出去 (此时,基本的RPC操作就完成了)

4. 获取注册中心对象,把信息交给注册中心去管理。也就是发布、订阅等。 这一步大量的优化了RPC处理集群的复杂过程。

服务端源码分析

测试代码:

package com.enjoy;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.enjoy.service.DemoService;
import com.enjoy.service.ZkDemoServiceImpl;
import org.junit.Test;

import java.io.IOException;

/**
 * 注册中心动态服务
 */
public class RpcRegistryProtocolTest {
    ExtensionLoader<Protocol> protocolLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
    ExtensionLoader<ProxyFactory> proxyLoader = ExtensionLoader.getExtensionLoader(ProxyFactory.class);

    //注册中心服务--zk的URL
    final URL registryUrl = URL.valueOf("registry://192.168.0.105:2181/com.alibaba.dubbo.registry.RegistryService?registry=zookeeper");

    // 模拟服务器的URL连接,服务端、消费端用的是同一个URL
    // 支持的协议:dubbo,http,hessian,rmi
    URL serviceUrl = URL.valueOf("dubbo://127.0.0.1:9001/com.enjoy.service.DemoService?application=abcd");
    @Test
    public void serverRpc() throws IOException {

        DemoService service = new ZkDemoServiceImpl("peter");
        //代理对象
        Protocol protocol = protocolLoader.getAdaptiveExtension();
        ProxyFactory proxy = proxyLoader.getAdaptiveExtension();

        serviceUrl = serviceUrl.setPort(9001);
        serviceUrl = serviceUrl.addParameter("loadbalance","first");
        serviceUrl = serviceUrl.addParameter("cluster","failsms");
        //连接到注册中心,把模拟的服务器的URL添加进连接注册中心的URL中
        URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl.toFullString());
        //暴露服务
        Invoker<DemoService> serviceInvoker = proxy.getInvoker(service, DemoService.class, newRegistryUrl);
        Exporter<DemoService> exporter = protocol.export(serviceInvoker);
        System.out.println("server 启动协议:"+serviceUrl.getProtocol());
        // 保证服务一直开着
        System.in.read();
        exporter.unexport();
    }
}

下面我就对照服务流程逐步的去拆解代码。

关于静态代理对象,代码是相同的,我直接贴出代码。这不是重点流程,想要深入的可以继续跟:

 那么我就贴出生成的2个静态代理对象的代码:

Protocal代理的源码

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {

	public void destroy() {
		throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	
	public int getDefaultPort() {
		throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	
	public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg1 == null) 
			throw new IllegalArgumentException("url == null");
			
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
			
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}
	
	public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
			
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );	//url没有指定处理类,默认用dubbo的。如果url中配置了处理类,则使用url中自带的
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
			
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader
															.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);  //又是dubbo spi思想
		
		return extension.export(arg0);	//实际的业务处理类
	}
}

ProxyFactory源码:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {

	public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();	
		String extName = url.getParameter("proxy", "javassist");
		if(extName == null)
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
			
		com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
																					.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
		return extension.getProxy(arg0);
	}
	
	public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0, boolean arg1) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
		String extName = url.getParameter("proxy", "javassist");
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
			
		com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
																				.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
		return extension.getProxy(arg0, arg1);
	}
	
	public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg2 == null) 
			throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg2;
		String extName = url.getParameter("proxy", "javassist");  //如果URL中有proxy,就根据URL配置的信息获取处理类。如果没有,那就是用默认的处理类javassist
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
		com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
													.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName); 	//dubbole spi思路
		return extension.getInvoker(arg0, arg1, arg2);		//调用具体的子类
	}
}

这两个静态代理是通过字符串的形式给拼接出来的,内存中是有的,但是我们无法进行debug操作,只能对照这代码进行分析。

获取invoker对象

 我们知道,proxy是一个静态代理对象,是有字符串拼接出来的并且在内存中是存在的,因此需要接口上方贴出的2个代理对象的源码进行分析。

具体步骤

1. 我们调用的是ProxyFactory的代理对象的getInvoker方法

2. 我们把断点打到 JavassistProxyFactory 类的对应方法处,进入断点并且封装了一个invoker对象进行返回

服务暴露:

1. 我们调用的是Portocal对象的静态代理类的export方法

 2. 直接把断点打到 RegistryProtocal类的export方法上

3. 重点分析服务暴露doLocalExport方法

其实,所谓的服务暴露,就是将需要暴露的invoker对象与协议进行绑定,并不是真的将invoker对象写到注册中心去。 

在步骤2中,我们也提及到了注册与发布,其实他们操作的也都是URL信息而已

注册中心,写入的只是服务器的 URL信息,根据不同的URL信息可以找到不同的invoer对象,然后由invoker对象再解析成对应的业务对象与方法进行调用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值