Dubbo 基本介绍与项目搭建(SPI、泛化概念)

why-为什么需要Dubbo?

单一应用架构 --> 垂直应用架构 --> 分布式服务架构 --> 流动计算架构 --> RPC

单一应用架构:

在这里插入图片描述
当流量很小时,只需要一个应用,所有模块都部署在一台服务器即可满足要求且开发、运维简单、数据库CRUD简单

垂直应用架构

在这里插入图片描述
当流量逐渐增大,单一应用的增加机器无法满足访问量需求,需要将应用进行垂直拆分为多个互不相干的应用,以提升效率。

分布式服务架构

在这里插入图片描述
当垂直应用越来越多,应用之间不可避免的要交互,将核心业务抽离出来作为独立的服务,使前端能够快速的响应多变的市场需求。

面向服务的架构(SOA)

在这里插入图片描述
随着服务越来越多,服务之间的调用和依赖变得复杂,因此诞生了面向服务的架构体系,也衍生除了一系列相应的技术来简化这些服务间的调用,如Dubbo

what-Dubbo主要有哪些能力?

  • 面向接口代理的高性能RPC调用,开发者可以就像调用本地方法一样调用远程方法
  • 智能负载均衡,内置多种负载均衡策略
  • 服务自动注册与发现(依靠Zookeeper)
  • 高度可扩展能力
  • 运行期流量调度
  • 可视化的服务治理与运维

how-怎么做到的

在典型的RPC使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中RPC指明了程序如何进行网络传输和序列化
在这里插入图片描述
这时典型的RPC基础架构

  1. 都有一个注册中心,server来注册服务,client发现服务
  2. client调用模块中有负载均衡、容错机制、透明等
  3. 在client中会对数据序列化、编码后进行网络传输,在server接收到调用请求后会进行解码、反序列化后执行方法调用

接下来我们看看RPC框架 Dubbo 中的架构概览
在这里插入图片描述

  • 节点角色说明

    • Provider:暴露服务的服务提供方
    • Consumer:调用远程服务的服务消费方
    • Registry:服务注册与服务发现的注册中心
    • Monitor:统计服务的调用次数和调用时间的监控中心
    • Container:服务运行容器
  • 调用关系说明

  1. 服务容器负责启动,加载,运⾏服务提供者。
  2. 服务提供者在启动时,向注册中⼼注册⾃⼰提供的服务。
  3. 服务消费者在启动时,向注册中⼼订阅⾃⼰所需的服务。
  4. 注册中⼼返回服务提供者地址列表给消费者,如果有变更,注册中⼼将基于⻓连接推送变更数
    据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选⼀台提供者进⾏调⽤,如果调⽤
    失败,再选另⼀台调⽤。
  6. 服务消费者和提供者,在内存中累计调⽤次数和调⽤时间,定时每分钟发送⼀次统计数据到监
    控中⼼

Dubbo 入门Demo

在这里插入图片描述

  1. 创建api模块,供provider、consumer调用

    import java.io.Serializable;
    
    /**
     * 订单
     */
    public class OrderDTO implements Serializable {
    
        private Long orderId;
    
        private String orderNo;
    
        private String desc;
    
        public Long getOrderId() {
            return orderId;
        }
    
        public void setOrderId(Long orderId) {
            this.orderId = orderId;
        }
    
        public String getOrderNo() {
            return orderNo;
        }
    
        public void setOrderNo(String orderNo) {
            this.orderNo = orderNo;
        }
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
    
        public OrderDTO(Long orderId, String orderNo, String desc) {
            this.orderId = orderId;
            this.orderNo = orderNo;
            this.desc = desc;
        }
    
        @Override
        public String toString() {
            return "Order{" +
                    "orderId=" + orderId +
                    ", orderNo='" + orderNo + '\'' +
                    ", desc='" + desc + '\'' +
                    '}';
        }
    }
    
    

    创建OrderService接口

    import com.siqi.dubbo.api.model.OrderDTO;
    
    /**
     * 订单服务
     **/
    public interface OrderService {
    
        OrderDTO getOrder(String orderNo);
    }
    
    
  2. 创建Provider模块

    • pom.xml 中增加依赖

              <!--依赖于api模块-->
              <dependency>
                  <groupId>com.siqi</groupId>
                  <artifactId>dubbo-api</artifactId>
                  <version>1.0-SNAPSHOT</version>
              </dependency>
              
              <dependency>
                  <groupId>org.apache.dubbo</groupId>
                  <artifactId>dubbo</artifactId>
                  <version>2.7.3</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.dubbo</groupId>
                  <artifactId>dubbo-registry-zookeeper</artifactId>
                  <version>2.7.3</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.dubbo</groupId>
                  <artifactId>dubbo-metadata-report-zookeeper</artifactId>
                  <version>2.7.3</version>
              </dependency>
      
    • 配置provider

      <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
             xmlns="http://www.springframework.org/schema/beans"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
             http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
      
          <dubbo:application name="demo-xml-provider" />
      
          <dubbo:registry id="registry" address="zookeeper://127.0.0.1:2181"/>
      
          <dubbo:protocol name="dubbo" port="20880"/>
      
          <dubbo:service interface="com.siqi.dubbo.api.service.OrderService" ref="orderService"/>
      
          <bean id="orderService" class="com.siqi.dubbo.service.OrderServiceImpl"/>
      
          <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
      </beans>
      
    • 创建OrderService接口的实现类OrderServiceImpl

      import com.siqi.dubbo.api.model.OrderDTO;
      import com.siqi.dubbo.api.service.OrderService;
      
      /**
       * 订单服务
       *
       **/
      public class OrderServiceImpl implements OrderService {
      
          @Override
          public OrderDTO getOrder(String orderNo) {
              System.out.println("Provider OrderService getOrder start, orderNo:" + orderNo);
              OrderDTO orderDTO = new OrderDTO(1L, "no1", "订单1");
              System.out.println("Provider OrderService getOrder end, orderNo:" + orderNo + ", orderDTO:" + orderDTO.toString());
              return orderDTO;
          }
      }
      
    • dubbo-provider模块启动类

      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class DubboXMLProvider {
          public static void main(String[] args) throws Exception {
              ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-provider.xml");
              context.start();
      
              System.out.println("xml provider 启动ing");
              System.in.read();
          }
      }
      

      在provider模块中会对外暴露OrderServiceImpl服务

  3. dubbo-consumer模块

    • pom.xml增加依赖

              <!--引入api模块-->
              <dependency>
                  <groupId>com.siqi</groupId>
                  <artifactId>dubbo-api</artifactId>
                  <version>1.0-SNAPSHOT</version>
              </dependency>
              
              <dependency>
                  <groupId>org.apache.dubbo</groupId>
                  <artifactId>dubbo</artifactId>
                  <version>2.7.3</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.dubbo</groupId>
                  <artifactId>dubbo-registry-zookeeper</artifactId>
                  <version>2.7.3</version>
              </dependency>
              <dependency>
                  <groupId>org.apache.dubbo</groupId>
                  <artifactId>dubbo-metadata-report-zookeeper</artifactId>
                  <version>2.7.3</version>
              </dependency>
      
    • 配置consumer

      <?xml version="1.0" encoding="UTF-8"?>
      
      <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
             xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
             http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
      
          <context:component-scan base-package="com.siqi.dubbo.user.impl" />
      
          <dubbo:application name="demo-xml-consumer" />
      
          <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
      
          <dubbo:reference id="orderService" interface="com.siqi.dubbo.api.service.OrderService" check="false"/>
      
      </beans>
      
      
    • 在consumer模块中的UserServiceImpl中RPC调用另一个模块的OrderServiceImpl

      public interface UserService {
      
          String getOrderInfo(String orderNo);
      }
      
      
      import com.siqi.dubbo.api.model.OrderDTO;
      import com.siqi.dubbo.api.service.OrderService;
      import com.siqi.dubbo.user.UserService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      
      @Service
      public class UserServiceImpl implements UserService {
      
          @Autowired
          private OrderService orderService;
      
          @Override
          public String getOrderInfo(String orderNo) {
              System.out.println("Consumer UserService getOrderInfo start, orderNo:" + orderNo);
      
              OrderDTO orderDTO = orderService.getOrder(orderNo);
              System.out.println("Consumer orderService.getOrderInfo end, orderNO:" + orderNo + ",orderDTO:" + orderDTO);
              return orderDTO.toString();
          }
      }
      
      
  4. 启动一个zookeeper注册中心

  5. 启动DubboXMLProvider

  6. 启动DubboXMLConsumer

  7. 最终可以看到订单服务调用成功且就像调用本地api一样方便

Dubbo 负载均衡策略

  • Random LoadBalance
    • 随机,按权重设置随机概率。
    • 在⼀个截⾯上碰撞的概率⾼,但调⽤量越⼤分布越均匀,⽽且按概率使⽤权重后也⽐较均匀,有利于动态调整提供者权重。
  • RoundRobin LoadBalance
    • 轮询,按公约后的权重设置轮询⽐率。
    • 存在慢的提供者累积请求的问题,⽐如:第⼆台机器很慢,但没挂,当请求调到第⼆台时就卡在那,久⽽久之,所有请求都卡在调到第⼆台上。
  • LeastActive LoadBalance
    • 最少活跃调⽤数,相同活跃数的随机,活跃数指调⽤前后计数差。
    • 使慢的提供者收到更少请求,因为越慢的提供者的调⽤前后计数差会越⼤。
  • ConsistentHash LoadBalance
    • ⼀致性 Hash,相同参数的请求总是发到同⼀提供者。
    • 当某⼀台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
      算法参⻅: http://en.wikipedia.org/wiki/Consistent_hashing
      缺省只对第⼀个参数 Hash,如果要修改,请配置 <dubbo:parameter key=“hash.arguments” value=“0,1” />
      缺省⽤ 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes” value=“320” />

扩展

API与SPI

在这里插入图片描述

API(Application Programming Interface):大多数情况下,都是实现方定制接口并完成接口的不同实现,调用方仅仅使调用,无法定制

SPI(Service Provider Interface):调用方来定制实现接口,服务方调用接口,常见的例子有:

  1. 数据库驱动Driver
  2. 日志Log
  3. Dubbo扩展点开发
  4. Spring Boot自动装配机制

JDK SPI与Dubbo SPI的区别

JDK SPI:

  • 配置:

    jdk.impl.Dog
    jdk.impl.Cat
    
  • 原理:

    1. 通过读取META-INF/services路径读取配置文件
    2. 获取到多个类的全路径名
    3. 使用类加载器加载类的全路径
  • 不足:

    1. 无法按需加载。如果仅需要一个实现类,ServiceLoader还是会将接口的实现类全部载入并实例化一遍,如果我们只需要一个,其它不需要的,这样就会造成资源消耗浪费
    2. 不具备IOC功能,如果一个接口实现类依赖复杂,如何实例化?
    3. serverLoader非线程安全

Dubbo SPI:

  • 配置
    man=dubbo.impl.Man
    woman=dubbo.impl.Woman
    
  • 原理:
    1. 先从缓冲中获取实例,如果没有则创建实例,接下去的创建实例都是线程安全的synchronized代码块
    2. 加载读取配置文件的所有 class,保存在一个Map<String, Class<?>>对象
    3. 根据不同的name读取接口实现类class
    4. 利用dubbo ioc注入接口实现类的实例
    5. 初始化dubbo组件生命周期并维护到缓存中

泛化调用及原理

泛化调用就是不需要依赖于接口或api模块的jar包,可以按照类的全限定名进行RPC调用

// API⽅式 引⽤远程服务
// 该实例很重要,⾥⾯封装了所有与注册中⼼及服务提供⽅连接,请缓存
ReferenceConfig<GenericService> reference = new referenceConfig<GenericService>();
// 弱类型接⼝名
reference.setInterface("com.end.dubbo.api.service.OrderService");
// 声明为泛化接⼝
reference.setGeneric(true);
// ⽤com.alibaba.dubbo.rpc.service.GenericService可以替代所有接⼝引⽤
GenericService genericService = reference.get();
Object result = genericService.$invoke("getOrder", new String[] {java.lang.String" }, new Object[]{ "ccc" });
System.out.println(result);

总结

本文介绍了dubbo的发展历史以及为什么需要dubbo,并使用一个最简单的demo搭建一个dubbo项目;并介绍了dubbo的SPI实现原理、泛化调用等概念。在下一篇将深入介绍dubbo源码分析

参考

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我思知我在

原创不易,多多一键三连

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

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

打赏作者

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

抵扣说明:

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

余额充值