浅谈Dubbo核心概念及架构流程

笔者碎碎言,我们学习Dubbo应该学的是什么?
笔者是一名业务开发,认为一切目的都要为我们的目标服务,即日常工作有帮助,所以笔者今天应该学的是它的源码实现流程、设计思想、扩展点,过于细节的去理解记忆源码细节ROI则太低。

  • 源码实现流程:让我们对Dubbo有一个整体的了解,日常遇到框架报错,可以更快定位原因
  • 设计思想:在面对类似的复杂的业务场景时,我们可以参考Dubbo设计,毕竟Dubbo已经给出了一个相对最优解
  • 扩展点:可以帮助我们在日常开发中借助三方框架更加方便的实现我们的功能。以我的亲身经历来说,就借助过Dubbo的Filter扩展链,完成对标记了@Deprecated接口的流量收集。还有泛化调用等场景

官网地址

前言

Dubbo源码主体流程可以总结为:服务提供者将编写的业务Service服务,按照某种协议序列化到注册中心,标记当前服务的网络资源所在的位置;服务消费者根据指定的key(version+interfaceName+group)去注册中心中找到对应的序列化数据,再将数据反序列化为服务消费者能够识别的格式,最终对目标资源发起调用。以此达到我们调用远程服务变成跟调用本地服务一样。

整体功能完成的基础上,Dubbo在设计上对扩展开放,一些比较重点的节点都能够进行扩展。如:自定义注册中心、自定义配置中心、自定义序列化协议、请求过滤器链Fiter等。一些个性功能的设计也是基于扩展点完成,如:mock机制、心跳机制、泛化掉用、异步调用、异常重试机制、回声测试等



重要概念

1、SPI

Dubbo的SPI机制与Java原生的SPI基本类似,但功能更加丰富。

  1. 使用@Adaptive注解来指定某个类为某个接口的代理类,Dubbo在生成自适应扩展点对象时,实际上生成的就是@Adaptive注解所注解的类的实例对象
  2. 实现了一套简单的AOP,如果一个接口的扩展点中包含了多个Wrapper类,那么在Dubbo在实例化完某个扩展点后,就会利用这些Wrapper类对这个实例进行包裹,比如:现在有一个DubboProtocol的实例,同时对于Protocol这个接口还有很多的Wrapper,比如ProtocolFilterWrapper、ProtocolListenerWrapper,那么,当对DubboProtocol的实例完成了IOC之后,就会先调用new ProtocolFilterWrapper(DubboProtocol实例)生成一个新的Protocol的实例,再对此实例进行IOC,完了之后,会再调用new ProtocolListenerWrapper(ProtocolFilterWrapper实例)生成一个新的Protocol的实例,然后进行IOC,从而完成DubboProtocol实例的AOP(类比装饰器模式,包一层)

基本用法如下:表示获取"dubbo"对应的Protocol扩展点。Protocol是一个接口。

ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol http = extensionLoader.getExtension("dubbo");
System.out.println(http);

2、ServiceBean

ServiceBean表示一个Dubbo服务,相关参数含义:

  1. ref,表示服务的具体实现类
  2. interface,表示服务的接口
  3. parameters,表示服务的参数(@Service注解中所配置的信息)
  4. application,表示服务所属的应用
  5. protocols,表示服务所使用的协议
  6. registries,表示服务所要注册的注册中心

补充:在扫描到一个@Service注解后,会得到两个Bean:

  1. 一个就是服务实现类本身一个Bean对象
  2. 一个就是对应的ServiceBean类型的一个Bean对象

3、URL

官网:Dubbo 中的 URL 统一模型

定义了服务资源的协议、参数等信息。主要用于在各个扩展点之间传递数据,组成此 URL 对象的具体参数如下:

  • protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk
  • username/password:用户名/密码
  • host/port:主机/端口
  • path:接口名称
  • parameters:参数键值对

dubbo 认为 protocol,username,passwored,host,port,path 是主要的 URL 参数,其他键值对存放在 parameters 之中。示例如下:

dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000
描述一个 dubbo 协议的服务

zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=1214&qos.port=33333&timestamp=1545721981946
描述一个 zookeeper 注册中心

consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=1209&qos.port=33333&side=consumer&timestamp=1545721827784
描述一个消费者

4、Invoker

属于Dubbo的核心模型,在代码实现层面类比递归调用的责任链设计模式

  • 在服务提供方Invoker是javassist创建的服务类的实例,可以实现调用服务类内部的方法和修改字段。

  • 在服务消费方的Invoker是基于Netty的客户端。

通过服务消费方Netty客户端获得服务提供方创建的服务类实例,而后消费方为保护服务类就需要为其创建代理类,这样就可以在不实例化服务类情况下安全有效的远程调用服务类内部方法并且得到具体数据了。

常见的Invoker:

  1. MockClusterInvoker: 完成Mock功能,由MockClusterWrapper生成,MockClusterWrapper是Cluster接口的包装类,通过Cluster.join()方法得到MockClusterInvoker

  2. FailoverClusterInvoker:完成集群容错功能,是MockClusterInvoker的下级

  3. RegistryAwareClusterInvoker:如果指定了多个注册中心,那么RegistryAwareClusterInvoker完成选择默认的注册中心的进行调用,如果没有指定默认的,则会遍历注册中心进行调用,如果该注册中心没有对应的服务则跳过。

  4. DubboInvoker:完成Dubbo协议底层发送数据

  5. ProtocolFilterWrapper$CallbackRegistrationInvoker:完成对filter的调用,ProtocolFilterWrapper是Protocol接口的包装类,通过Protocol.refer()方法得到CallbackRegistrationInvoke。



整体流程

1、架构图

左侧为服务消费者,右侧为服务提供者

  1. service层,接口层,给服务提供者和消费者来实现的
  2. config层,配置层,主要是对dubbo进行各种配置的
  3. proxy层,服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton
  4. registry层,服务注册层,负责服务的注册与发现
  5. cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
  6. monitor层,监控层,对rpc接口的调用次数和调用时间进行监控
  7. protocol层,远程调用层,封装rpc调用
  8. exchange层,信息交换层,封装请求响应模式,同步转异步
  9. transport层,网络传输层,抽象mina和netty为统一接口
  10. serialize层,数据序列化层,网络传输需要

请添加图片描述


2、调用链路

左侧为服务消费者,右侧为服务提供者

请添加图片描述