一文学习Dubbo

Dubbo

基础

  • Dubbo 提供了从服务定义、服务发现、服务通信到流量管控等几乎所有的服务治理能力,并且尝试从使用上对用户屏蔽底层细节,以提供更好的易用性。Dubbo 中,我们提到服务时,通常是指 RPC 粒度的、提供某个具体业务增删改功能的接口或方法
  • Dubbo是一个SpringBoot项目(因此也像Spring一样作了拆分、系统耦合度非常低且提供了很多SPI和扩展接口)、通过Netty搭建线程模型
  • 点对点的服务通信是 Dubbo 提供的另一项基本能力
    • 同步的 Request-Response 是默认的通信模型
    • 消费端异步请求(Client Side Asynchronous Request-Response)
    • 提供端异步执行(Server Side Asynchronous Request-Response)
    • 消费端请求流(Request Streaming)
    • 提供端响应流(Response Streaming)
    • 双向流式通信(Bidirectional Streaming)
  • Dubbo通过不同的注册中心可以根据需要选择AP或者CP;Dubbo提供了很多高级特性(由于其高可扩展性,可以不断添加自己的特性)
    • 非完整的系统容错支持服务限流、服务降级、服务熔断(需要借助第三方组件)、超时服务、重试;

      • 服务降级:通过mock机制进行服务降级控制;

        • 可以通过设置本地存根或者自定义返回数据,决定降级的服务(熔断的服务)的熔断处理策略
      • 服务熔断:可以借助Hystrix 实现,本身不支持服务熔断(因为dubbo的服务级别是方法,所以其熔断支持级别也是方法);如果使用mock机制实现,则不能区分服务的优先级,导致所有服务均会熔断;

        • 通过Hystrix的熔断处理策略进行处理
      • 服务限流:通过控制连接数(或者处理数),控制服务的最大并发;

      • 超时服务允许对于服务的消费者和提供者进行超时设置(超时的服务将抛出异常,一般消费者设置的时间应该略大于生产者,因为还有网络传输时间);

      • 重试:**Dubbo默认重试次数为2次,当前服务调用失败后会进行两次次数;**后续处理策略就是会重试调用其它机器上的服务提供者,加上第一次调用默认是重试2次,总共调用3次。

    • 完整的高可用支持:负载均衡、根据注册中心进行服务可用性管理;

      • 负载均衡算法:

        • 轮询(roundrobin);
        • 一致性hash(consistenthash):相同参数的请求总是发到同一提供者。
        • 当前最少并发处理的服务(LeastActive);
      • 服务的可用性监控通过和注册中心实现

    • 灰度发布:版本控制(允许给服务追加版本号,消费者根据版本号调取服务)

    • 泛化机制:GenericService是Dubbo提高的全局服务接口,通常进行服务测试,在泛化调用机制下,全球和响应均为Map格式

    • 支持异步调用:由于其线程模型是netty,通过netty可以实现异步处理,异步响应;

Dubbo 提供的基础能力包括:

  • 版本控制
  • 服务发现
  • 流式通信
  • 负载均衡
  • 流量治理
  • ……
//imgs/architecture.png
  • 正如上图:dubbo有四个基本角色:注册中心(zookeeper等)、服务消费者(通过dubbo获取注册的服务)、服务提供者(向注册中心注册)、监控(dubbo的一部分)
使用

SpringBoot

  • 俩个注解:@Service、@Reference;可以通过俩者进行服务容错和高可用及其他高级特性配置
    • @Service标记为dubbo服务(向注册中心注册);
    • @Reference标记为获取注册中心服务;
  • **yaml核心配置(**也可以使用官方文档的xml):
    • application:应用级别的配置,一般配置外网地址、服务名、多注册中心配置等
      • image-20220402171657626
      • image-20220402171734073
    • module:模块消息配置(没用过)
      • image-20220402171601002
    • registry:注册中心配置(注册中心协议、地址、端口、账号密码等)
      • image-20220402171943010
    • consumer:消费者配置
    • provider:服务提供者配置
    • monitor:监控中心配置
    • protocol:协议配置(dubbo允许通过rest、dubbo、multilateral等协议进行通讯)

dubbo特性原理

  • dubbo通过SPI机制支持序列化扩展、多种协议(自定义协议)、多种注册中心(自定义注册中心)、集群扩展;
  • dubbo通过动态代理+Spring的Schema机制实现:直接进行远程调用(无入侵的使用远程调用);
  • dubbo通过分层:将集群容错、注册、监控、协议、传输、序列化进行解耦合,使得可以对任意一个层级进行扩展

核心原理

结构图
image-20220402143556635
服务注册和发现

be16315c4fdcc36869b163203e59e3d3.png

  • Proxy:实现透明的调用
  • Registry:自动的服务注册和发现;Registry和Monitor实际上不算一层,而是一个独立的节点。
  • Cluster:集群的负载均衡
  • Monitor:服务监视器
  • Protocol:根据协议进行RPC远程过程调用
  • Remoting的三层:主要为各种协议扩展的层,在JVM的RMI协议不需要Remoting
    • Exchange:封装请求响应模式,同步转异步,以 Request 和 Response 为中心
    • Transport:Netty网络传输层
    • Serialize:序列化和反序列化
服务注册的过程
  • 首先获取DubboBeanDefinition、生成Bean对象
  • 然后是将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。
  • 根据配置的协议、暴露到的注册中心的位置进行服务暴露(无配置中心,例如广播方式,则进行相应端口暴露)
服务发现的过程
  • 首先根据配置解析出注册中心地址(没有则直接解析出服务的地址)
    • 有注册中心到注册中心获取连接:registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode(“consumer://consumer-host/com.foo.FooService?version=1.0.0”)
    • 无注册中心直接拼接得到service-host/com.foo.FooService?version=1.0.0,再和下面的协议组成完整的连接
  • 然后根据协议生成URL,通过代理进行远程调用;
  • 通过Netty和服务提供者建立相应的连接(如果是dubbo协议默认使用长连接)
Dubbo内核
  • Dubbo的RPC主要是通过Protocol实现、Protocol是Dubbo的核心层
  • Dubbo是根据调用的URL进行功能的选择实现的,根据不同的URL可以使得Dubbo动态的实现各种功能;
  • Dubbo的核心除了不变的两个API层,其他均通过SPI实现高度的可扩展性;Dubbo的内核由SPI的四种机制组成:DubboSPI机制Adaptive机制Activate机制Wrapper机制
  • SPI机制:Dubbo除了在Service和Config没有也不需要SPI外,其在RPC和Retoming都运行通过SPI机制自主根据URL进行扩展;
    • 例如自定义注册中心Registry、则需要重新对于Registry层进行SPI扩展即可;【同理还有线程模型、序列化方式、集群的负载方式等】
  • Adaptive机制:自适应扩展,由于Dubbo各层都提供了很多实现、很多时候并不需要都用到,所以Dubbo提出了自适应扩展(即在URL中需要的时候才进行类加载和初始化)
    • @Adapative 修饰的 SPI 接口扩展类称为 Adaptive 类(自适应类),表示该 SPI 扩展类会按照该类中指定的方式获取,即用于固定实现方式。其是装饰者设计模式的应用。
    • 被@Adapative 修饰 SPI 接口中的方法称为 Adaptive 方法。在 SPI 扩展类中若没有找到Adaptive 类,但系统却发现了 Adapative 方法,就会根据 Adaptive 方法自动为该 SPI 接口动态生成一个 Adaptive 扩展类,并自动将其编译。
  • Activate机制主动多个激活扩展机制;主要使用在有多个扩展点实现、需要同时根据不同条件被激活的场景中,如Filter需要多个同时激活,因为每个Filter实现的是不同的功能。【最常用到也是在Filter机制中】
  • Wrapper机制:类似于静态代理,每一个wrapper类:
    • 该类要实现 SPI 接口
    • 该类中要有 SPI 接口的引用
    • 必须有有参构造器,有参构造器有且只有一个参数:SPI接口
    • 在接口实现方法中要调用 SPI 接口引用对象的相应方法
    • 该类名称以 Wrapper 结尾

Protocol层

Protocol层主要实现类似Web的俩个功能:服务的暴露和引用、服务的过滤;这样就使得Dubbo尽管没有通过web容器提供服务,服务的暴露和引用是RPC的核心,同时尽管没有web容器但是通过Filter实现过滤和安全机制;

服务暴露和引用
  • Protocol层是Dubbo的核心层,是实现RPC的关键(服务的暴露和服务引用);

    • @SPI("dubbo")
      public interface Protocol {
          /**
          * 未配置端口默认端口
          */
          int getDefaultPort();
          
          /**
           * 用于远程调用的暴露接口,也就是Invoker转化成Exporter的接口
           * 1.协议在收到请求后要记录请求源地址RpcContext.getContext().setRemoteAddress();
           * 2.export()必须是幂等的,即导出同一个URL时调用一次和调用两次没有区别
           * 3.Invoker 实例是框架传入的,协议不用管
           * @param <T>     Service type 接口类型
           * @param invoker Service invoker 接口类型转成URL再转换成的Invoker
           */
          @Adaptive
          <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
       
          /**
           * 引用远程服务
           * 1.当用户调用从`refer()`调用返回的`Invoker`对象的`invoke()`方法时,协议需要对应执行`Invoker`对象的`invoke()`方法 
           * 2. 协议的责任是实现从 `refer()` 返回的 `Invoker`。 一般来说,协议在 `Invoker` 实现中发送远程请求。 
           * 3、当URL中设置了check=false时,实现一定不要抛出异常,而是在连接失败时尝试恢复。
           */
          @Adaptive
          <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
        
          /**
          * 销毁协议:
          * 1.取消本协议导出和引用的所有服务 
          * 2.释放所有占用的资源,例如:连接、端口等。
          * 3. 协议被销毁后仍可以继续导出和引用新服务
          */
      	void destroy();
      
      }
      
Filter机制
  • Filter接口是Dubbo的核心过滤器接口,实现该接口的类通过加入Dubbo的SPI文件;
    • Dubbo运行通过yaml配置文件、xml配置文件或者@Adaptive方式使得配置生效;
    • 在<dubbo:service filter=“”/>或<dubbo:reference filter=“”/>标签中使用filter属性来指定具体的filter名称,这种使用方式的作用级别只针对于所指定的某个provider或consumer;
    • 在<dubbo:provider filter=“”/>或<dubbo:consumer filter=“”/>标签中使用filter属性来指定具体的filter名称,这种使用方式的作用级别针对于所有的provider或consumer;
    • 在所声明的实现了Filter接口的类上使用@Activate注解来标注,并且注解中的group属性指定为providerconsumer
  • Dubbo在ProtocolFilterWrapper::buildInvokerChain将生效的过滤器链组织起来;
  • 在Filter实现类中可以通过Setter方式注入bean;

SPI&Adaptive机制

SPI机制
  • 在Spring的时候我们知道JAVA的SPI机制是通过ServerLoad调用线程的类加载器进行类加载,而且一次加载在全部SPI注入的实现类(META-INF/services/所有文件【文件名为接口全限定名、内容为各个实现全限定类名】)
  • Spring对于SPI的改进则是通过spring.factories进行SPI配置不再需要么个接口一个文件,同时实现了通过SpringFactoriesLoader获取指定类型的实现类全限定类名,调用方进行类加载,不需要全部类进行类加载;
  • dubbo的SPI机制和Spring的类似,可以按需加载,而且支持AOP和IOC、缓存机制等,实现了自适应扩展
    • 提供者配置文件路径:在依次查找的目录为
      META-INF/dubbo/internal
      META-INF/dubbo
      META-INF/services
    • 自适应扩展:Dubbo中存在很多的扩展类,这些扩展类不可能一开始就全部初始化,那样非常的耗费资源,所以我们应该在使用到该类的时候再进行初始化,在java语言中,拓展(类\方法)未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载,所以这种思路基于直接创建对象的角度是矛盾的,所以需要一种机制实现(自适应扩展)
    • 通过**@SPI、@Adaptive、@Activate对于URL进行自适应扩展**
    • image-20220402220737067
@Adaptive使用
  • @Adaptive:可以标记在方法上、也可以标记在类上;
    • 标记在类上的时候,将使得扩展点的实现类只能是标记类
    • 标记在方法上时,将根据URL传入的参数选择实现类、只有URL没有传入该参数的时候选择@SPI标记的默认实现类

例子

  • 扩展点及其实现类
//扩展类
@SPI("default")
public interface AdaptiveTest {
    //自适应扩展点,如果未指定参数名则使用 驼峰命名的点分形式
    //可以通过@Adaptive的value指定参数命名
    @Adaptive
    String adp(String msg, URL url);
}

//@SPI标记的默认实现类
public class DefaultAdaptiveTest implements AdaptiveTest {
    @Override
    public String adp(String msg, URL url) {
        return "default";
    }
}


//通过url选择的扩展点实现类
public class OneAdaptiveTest implements AdaptiveTest {
    @Override
    public String adp(String msg, URL url) {
        return "11111";
    }
}

public class TwoAdaptiveTest implements AdaptiveTest {
    @Override
    public String adp(String msg, URL url) {
        return "22222";
    }
}

//指定的扩展点实现类
@Adaptive
@Setter
@Getter
public class MyAdaptiveTest implements AdaptiveTest {

    String urlChoose;
    
    @Override
    public String adp(String msg, URL url) {
        //为了遵循修饰器模式的理念,@Adaptive实现类一般用于选择真正的实现类
        	//下面为了演示,直接返回"指定"
        ExtensionLoader<AdaptiveTest> loader = 
            					ExtensionLoader.getExtensionLoader(AdaptiveTest.class);
        AdaptiveTest choose;
        if(StringUtils.isEmpty(urlChoose)) {
            choose = loader.getDefaultExtension();
        } else {
            choose = loader.getExtension(urlChoose);
        }
        //增强
        System.out.print("指定");
        return choose.apt(msg,url);
    }
}
  • 注册扩展点实现类
    • 文件名:接口全限定名
#在没有@Adaptive类标注类前提下
#该类被扩展点@SPI(default)标记,所以没有参数或者传入参数为adaptive.test=default就会选择该实现类
default=XXXXXXX.DefaultAdaptiveTest
#如果url中带有参数:adaptive.test=one   则选择这个实现类
one=XXXXXXX.OneAdaptiveTest
#如果url中带有参数:adaptive.test=two  则选择这个实现类
two=XXXXXXX.TwoAdaptiveTest



#因为my是一个@Adaptive类,所以主要该类注册,就只会选择这个实现类
my=XXXXXXX.MyAdaptiveTest
@Activate使用

核心组件

image-20220614125232363

  • 在开始深入学习各个组件前,我们在使用Dubbo时仅仅只需要一个接口即可,显然需要一个代理帮助我们调用进行远程调用、数据封装和解析;

下面就通过JAVA的API注册和获取服务的角度学习各个组件:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舔猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值