Dubbo

概念

  • 简介

    Dubbo 是一个高性能的RPC 框架,解决了分布式中的调用问腿

  • 优点: 解决了分布式系统中互相调用的问题

  • 缺点: 假设由100 台服务器50 台用户业务服务器,50 台订单业务服务器,但是在上线后发现,用户服务器使用率很小,但是订单服务器压力很大,最佳匹配比应该是1:4,这时候就要求我们还有一个统一管理的调度中心

  • 为什么Dubbo 说自己性能高

    高性能要从底层的原理说起,既然是一个RPC 框架,主要干的就是远程过程(方法)调用,那么提升性能就要从最关键,最耗时的两个方面入手: 序列化和网络通信

    • 序列化: 我们学习Java 网络开发的时候就知道,本地的对象要在网络上传输,必须要实现Serializable 接口,也就是必须序列化,我们序列化的方案很多:xml,json,二进制流其中效率最高的就是二进制流(因为计算机就是二进制的),然而Dubbo 采用的就是效率最高的二进制
    • 网络通信: 不同于HTTP 需要进行7 步走(三次握手和四次挥手),Dubbo 采用Socket 通信机制,一步到位,提升了通信效率,并且可以建立长连接,不用反复连接,直接传输数据
  • 架构

    摘自官网
    • 服务提供者(provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务
    • 服务消费者(Consumer): 调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选调另一台
    • 注册中心(Registry): 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更变数据给消费者
    • 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
  • 调用关系说明

    • 服务容器负责启动,加载,运行服务提供者

    • 服务提供者在启动时,向注册中心注册自己提供的服务

    • 服务消费者在启动时,向注册中心订阅自己所需的服务

    • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

    • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用

    • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

Dubbo 支持的协议

  • 协议

    支持多种协议:dubbo,hessian,rmi,webservice,thrift,memcached,redis,dubbo官方推荐dubbo 协议,dubbo 协议默认端口20880

    使用dubbo 协议,spring 配置文件加入:<dubbo:protocol name="dubbo" port="20880" />

  • 优点

    采用NIO 复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)

  • 缺点

    大文件上传时,可能出现问题(不使用Dubbo 实现文件传输)

日志配置

  • 配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # DEBUG < INFO < WARN < ERROR < FATAL
    # Global logging configuration
    log4j.rootLogger=info, stdout,file
    # My logging configuration...
    #log4j.logger.com.tocersoft.school=DEBUG
    #log4j.logger.net.sf.hibernate.cache=debug
    ## Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n

    log4j.appender.file=org.apache.log4j.FileAppender
    log4j.appender.file.File=../logs/iask.log
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
  • 依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--日志-->
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
    </dependency>

Dubbo-Admin

  • 前提环境:Node、Maven,dubbo-admin(master分支), 使用 cmd 执行

  • 简介

    • dubbo-admin 管理平台,是图形化的服务管理页面
    • 从注册中心获取到所有的提供者/消费者进行配置管理
    • 路由规则,动态配置、服务降级、访问控制、权重调整
    • dubbo-admin 是一个前后端分离的项目,前端使用vue,后端使用springboot
    • 安装dubbon-admin 其实就是部署该项目
  • 下载

    dubbo-admin(master):https://github.com/apache/dubbo-admin/tree/master
  • 配置注册中心(dubbo-admin > dubbo-admin-server >resource>*.properties)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # zookeeper 修改 ip 地址后 通过 ./zkCli,sh -server ip:port 可以连接(默认加载2181)
    # ip 地址是部署 zookeeper 环境的 ip 地址与端口
    admin.registry.address=zookeeper://43.142.80.9:2181
    admin.config-center=zookeeper://43.142.80.9:2181
    admin.metadata-report.address=zookeeper://43.142.80.9:2181

    # 设置最大等待时间为10
    dubbo.metadata-report.timeout=10000
    # 配置中心连接时间改为10
    dubbo.config-center.timeout=10000

  • dubbo-admin/ 目录下执行打包

    1
    2
    3
    4
    5
    6
    # 执行打包
    mvn clean package -Dmaven.test.skip=true
    # 过程中出现错误
    1. 代理问题(如果有可能, maven settings.xml 添加代理配置)
    2. node 插件下载问题,请直接删除项目中的 node 下载,在系统安装
    https://npm.taobao.org/mirrors/node/v14.6.0/(镜像下载地址)
    • 问题一

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <!-- maven settings.xml -->
      <proxies>
      <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <host>your proxy ip</host>
      <port>your proxy port</port>
      </proxy>
      </proxies>
      proxy
    • 问题二

      删除node 下载插件 再次执行mvn clean package -Dmaven.test.skip=true
    • 成功编译信息

      (删除项目中 Node 插件,这个node 的作用是在dubbo-admin-ui 执行下载,可以直接安装在系统上,手动执行npm install)
  • dubbo-admin-master\dubbo-admin-ui 执行npm install

  • dubbo-admin-master\dubbo-admin-distribution\target 下执行如下

    1
    2
    # 启动
    java -jar dubbo-admin-0.3.0.jar
    启动
  • dubbo-admin-master\dubbo-admin-ui 执行npm run dev

    npm run dev 启动UI
  • 访问UI

    默认roo 成功登录

RPC

分布式系统
  • 什么是分布式系统

    ​ 分布式系统是若干独立计算机(服务器)的集合,这些计算机对于用户来说就像单个相关系统,分布式系统是建立在网络之上的服务器端一种结构

    ​ 分布式系统总计算机可以使用不同的操作系统,可以云兄不同应用程序提供服务,将服务分散部署到多个计算机服务器上

RPC
  • 什么是RPC

    RPC 是指远程过程调用,是一种进程间通信方式,是一种技术思想,而不是规范,它允许程序调用另一个地址空间(网络的另一台机器上)的过程或函数,而不用开发人员显示编码这个调用的细节,调用本地方法和调用远程方法一样

  • RPC 特点

    • 简单: 使用简单,建立分布式应用更容易
    • 高效: 调用过程看起来十分清晰,效率高
    • 通用: 进程间通讯的方式,有通用的规则
  • RPC 原理

    RPC 原理
    • RPC 调用过程
      1. 调用方client 要使用右侧server 的功能(方法),发起对方法的调用
      2. client stub PRC 中定义的存根,看作是client 的助手,stub 把要调用的方法参数进行序列化,方法名称和其他数据包装起来
      3. 通过网络socket(网络通信的技术),把方法调用的细节内容发送给右侧的server
      4. server 端通过socket 接受请求的方法名称,参数等数据,传给stub
      5. server 端接到的数据由server stub(server 的助手)处理,调用server 的真正方法,处理业务
      6. server 方法处理完业务,把处理的结果对象Object 交给了助手,助手把Object 进行序列化,对象转为二进制数据
      7. server 助手把二进制数据交给网络处理程序
      8. 通过网络将二进制数据发送给client
      9. client 接数据,交给client 助手
      10. client 助手,接受数据通过反序列化为java 对象(Object),作为远程方法调用的结果
直连方式
  • 图解

    Consumer -> Provider 不使用注册中心
  • 实现目标

    直连方式访问
  • 实现步骤

    • 创建新项目

      • 添加模块(link-orderservice-provider: dubbo的服务提供者,提供接口,让消费者调用)

        1. 新建maven

        2. 加入依赖

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          <!-- Spring 依赖 -->
          <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.14.RELEASE</version>
          </dependency>
          <!-- dubbo 的依赖 -->
          <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>dubbo</artifactId>
          <version>2.6.2</version>
          </dependency>
          <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.24</version>
          </dependency>
        3. 新建保存数据的实体类Order,实现序列化接口

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          package com.coderitl.dubbo.domain;

          import lombok.Data;

          import java.io.Serializable;

          @Data
          public class Order implements Serializable {
          private static final long serialVersionUID = -4779766312604286029L;
          // 订单 id
          private String id;
          // 商品名称
          private String goodName;
          // 单价
          private float price;
          // 购买数量
          private int amout;
          }

        4. 新建业务接口OrderServcice,定义方法createOrder

        5. 新建业务接口的实现类,实现createOrder()

        6. 新建spring 的配置文件,通过容器创建和管理对象

          • 声明Dubbo 的服务名称
          • 声明服务的接口,暴露接口
          • 声明服务接口的实现类
        7. 新建测试类,测试spring 的配置文件

        8. 把服务提供者的jar,安装到本地的maven 仓库

      • link-main-web(quick) 创建消费者项目,调用服务提供者中接口的方法

        • 实现步骤
          1. 新建maven
          2. 加入依赖
          3. 定义业务接口和实现类
          4. 新建spring 的配置文件
            • 声明服务的名称
            • 声明要使用的远程接口额服务提供者)
            • 声明自定义的业务对象
          5. 新建测试通过消费者访问提供者
Dubbo服务化最佳实践
  • 分包

    建议将服务接口、服务模型、服务异常等均放在公共包中

  • 粒度

    服务接口尽可能最大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持

    服务接口建议以业务场景为单位划分,并对相近业务做抽离,防止接口数量爆炸

    不建议使用过于抽象的通用接口,Map query(Map) 这样的接口没有明确语义,会给后期维护带来不便

  • 版本

    每个接口都应该定义版本号,为后续不兼容升级提供可能,如:<dubbo:service interface="" version="1.0">

    建议使用两位版本号,要变更服务版本,先升级一半提供者为新版本,再将消费者全部升级为新版本,然后将剩下的一半提供者升为新版本

  • 项目修改

    • 实现目标

      实现目标

快速入门

  • 抽取公共接口dubbo-interface => jar模块

    1
    2
    3
    4
    5
    6
    package com.coderitl.service;

    public interface UserService {
    public String sayHello();
    }

  • 创建提供者模块dubbo-service =>webapp(war)

    • 创建公共接口的实现类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      package com.coderitl.service.impl;

      import com.coderitl.service.UserService;
      import org.apache.dubbo.config.annotation.Service;


      //@Service //将该类的对象创建出来,放到SpringIOC容器中 bean定义

      @Service // 将这个类提供的方法(服务)对外发布。将访问的地址 ip,端口,路径注册到注册中心中
      public class UserServiceImpl implements UserService {

      public String sayHello() {
      return "hello dubbo hello!~";
      }
      }

    • 创建配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      <!-- resources/spring/applicationContext.xml -->
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://dubbo.apache.org/schema/dubbo
      http://dubbo.apache.org/schema/dubbo/dubbo.xsd
      http://www.springframework.org/schema/context
      https://www.springframework.org/schema/context/spring-context.xsd">

      <!-- 被 dubbo 扫描注解代替 -->
      <!--<context:component-scan base-package="com.coderitl.service" />-->



      <dubbo:application name="dubbo-service"/>

      <dubbo:registry address="zookeeper://43.142.80.9:2181"/>

      <dubbo:annotation package="com.coderitl.service.impl" />

      </beans>
    • WEB.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://java.sun.com/xml/ns/javaee"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      version="2.5">


      <!-- spring -->
      <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:spring/applicationContext*.xml</param-value>
      </context-param>
      <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>


      </web-app>
    • 依赖

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>

      <groupId>com.coderitl</groupId>
      <artifactId>dubbo-service</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>



      <properties>
      <spring.version>5.1.9.RELEASE</spring.version>
      <dubbo.version>2.7.4.1</dubbo.version>
      <zookeeper.version>4.0.0</zookeeper.version>

      </properties>

      <dependencies>

      <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
      </dependency>

      <!--日志-->
      <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
      </dependency>
      <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.21</version>
      </dependency>




      <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo</artifactId>
      <version>${dubbo.version}</version>
      </dependency>

      <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>${zookeeper.version}</version>
      </dependency>

      <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-recipes</artifactId>
      <version>${zookeeper.version}</version>
      </dependency>


      <!--依赖公共的接口模块-->
      <dependency>
      <groupId>com.coderitl</groupId>
      <artifactId>dubbo-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
      </dependency>

      </dependencies>

      <build>
      <plugins>

      <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.1</version>
      <configuration>
      <port>8000</port>
      <path>/</path>
      </configuration>
      </plugin>
      </plugins>
      </build>
      </project>
  • 创建消费者模块dubbo-web => webapp(war)

    • 创建控制器

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      package com.coderitl.controller;

      import com.coderitl.service.UserService;
      import org.apache.dubbo.config.annotation.Reference;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      @RestController
      @RequestMapping("/user")
      public class UserController {

      // 注入 Service
      // @Autowired // 本地注入

      /*
      1. 从 zookeeper 注册中心获取 userService 的访问 url
      2. 进行远程调用 RPC
      3. 将结果封装为一个代理对象。给变量赋值
      */

      @Reference // 远程注入
      private UserService userService;


      @RequestMapping("/sayHello")
      public String sayHello(){
      return userService.sayHello();
      }

      }


    • 创建配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      <!-- resources/spring/springmvc.xml -->
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://dubbo.apache.org/schema/dubbo
      http://dubbo.apache.org/schema/dubbo/dubbo.xsd
      http://www.springframework.org/schema/context
      https://www.springframework.org/schema/context/spring-context.xsd">


      <mvc:annotation-driven/>
      <context:component-scan base-package="com.coderitl.controller"/>




      <dubbo:application name="dubbo-web">
      <dubbo:parameter key="qos.port" value="33333"/>
      </dubbo:application>

      <dubbo:registry address="zookeeper://43.142.80.9:2181"/>

      <dubbo:annotation package="com.coderitl.controller"/>


      </beans>
    • web.xml 文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://java.sun.com/xml/ns/javaee"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
      version="2.5">


      <!-- Springmvc -->
      <servlet>
      <servlet-name>springmvc</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

      <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/springmvc.xml</param-value>
      </init-param>
      </servlet>

      <servlet-mapping>
      <servlet-name>springmvc</servlet-name>
      <url-pattern>*.do</url-pattern>
      </servlet-mapping>


      </web-app>
    • 依赖

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>

      <groupId>com.coderitl</groupId>
      <artifactId>dubbo-web</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>


      <properties>
      <spring.version>5.1.9.RELEASE</spring.version>
      <dubbo.version>2.7.4.1</dubbo.version>
      <zookeeper.version>4.0.0</zookeeper.version>

      </properties>

      <dependencies>

      <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
      </dependency>

      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
      </dependency>

      <!--日志-->
      <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
      </dependency>
      <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.21</version>
      </dependency>




      <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo</artifactId>
      <version>${dubbo.version}</version>
      </dependency>

      <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>${zookeeper.version}</version>
      </dependency>

      <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-recipes</artifactId>
      <version>${zookeeper.version}</version>
      </dependency>

      <!--依赖公共的接口模块-->
      <dependency>
      <groupId>com.coderitl</groupId>
      <artifactId>dubbo-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
      </dependency>

      </dependencies>

      <build>
      <plugins>

      <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.1</version>
      <configuration>
      <port>9000</port>
      <path>/</path>
      </configuration>
      </plugin>
      </plugins>
      </build>
      </project>
    • 访问测试

      访问测试

注册中心

序列化

  • 问题: 两个机器传输数据,如何传输Java 对象?

    序列化
  • 实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import java.io.Serializable;

    @Data
    @AllArgsConstructor
    public class UserInfo implements Serializable {
    private Integer id;
    private String userName;
    private String address;
    }

  • 接口与实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 实现
    public UserInfo getUserById(int id) {
    UserInfo userInfo = new UserInfo(1, "coder-itl", "BJ");
    return userInfo;
    }

    // 控制器
    @RequestMapping("/getinfo")
    public UserInfo getInfo() {
    return userService.getUserById(1);
    }
  • 访问

    序列化实现
    在这里插入图片描述

地址缓存

  • 注册中心挂了,服务是否可以正常访问?
    • 可以,因为Dubbo 服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心
    • 当服务提供者地址放生变化时,注册中心会通知服务消费者

超时与重试

  • 添加位置
    • @Service(timeout=1000,retries=0) 单位毫秒,默认 1000ms
    • @Reference(timeout=1000)单位毫秒,默认 1000ms
    • 超时建议添加在服务提供方
    • 重试默认retries=2

多版本

  • 灰度发布

    部分更新,提供反馈 完全更新
  • Version:Dubbo中使用 Version 属性来设置和调用同一个接口的不同版本

    1
    2
    3
    4

    @Service(version="v1.0")

    @Reference(version="v1.0")

负载均衡

  • 配置负载均衡

    1
    2
    3
    4
    5
    6
    // weight 配置权重

    @Service(weight=100)

    @Reference(loadbalance="random")

集群容错

  • 集群容错模式

    • Failover Cluster: 失败重试, 默认值 ,当出现失败,重试其他服务器,默认重试2 (retries=2),使用retries 配置,一般用于读操作

      1
      @Reference(cluster = "failover ") // 默认
    • Failfast Cluster: 快速失败,只发起一次调用,失败立即报错, 通常用于写操作

    • Failsafe Cluster: 失败安全,出现异常时,直接忽略,返回一个空结果

    • Failback Cluster:失败自动恢复,后台记录失败请求,定时重发

    • Forking Cluster: 并行调用多个服务器,只要一个成功即返回

    • Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错

服务降级

  • 含义

    对重要的服务降级
  • 官网

    https://dubbo.apache.org/zh/docs3-v2/java-sdk/advanced-features-and-usage/service/service-downgrade/

    1
    @Reference(mock = "fail:return+null")
    • 配置mock="[fail|force]return|throw xxx"
      • fail force关键字可选,表示调用失败或不调用强制执行mock 方法,如果不指定关键字默认为fail
      • return 表示指定返回结果,throw 表示抛出指定异常
      • xxx 根据接口的返回类型解析,可以指定返回值或抛出自定义的异常

基于SpringBoot-Dubbo

  • 项目创建(聚合项目)

    • 添加模块dubbo-provider-8080

      • SpringBoot->Dubbo: https://github.com/apache/dubbo-spring-boot-project

      • 依赖

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        <dependencies>
        <!-- springboot 起步依赖 -->
        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- dubbo 起步依赖 -->
        <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>2.7.7</version>
        </dependency>
        <!-- zookeeper 客户端依赖 -->
        <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>5.1.0</version>
        </dependency>
        <!-- zookeeper 客户端依赖 -->
        <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>5.1.0</version>
        </dependency>

        </dependencies>
      • 缺少依赖添加技巧

        NoClassDefFoundError =>apache/curator/framework => apache-curator-framework 在仓库查找
      • 配置文件

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        # application.yml
        server:
        port: 8080

        dubbo:
        registry:
        address: zookeeper://43.142.80.9:2181 # 注册中心
        timeout: 600000 # 超时时间
        application:
        name: dubbo-provider-8080 # 服务名称,如果多个启动的服务进程命名一致,自动组成集群
        protocol:
        name: dubbo # 协议
        port: 20880 # 端口
        config-center: # 配置中心超时时间
        timeout: 600000
      • zookeeper 存储内容解读

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        /dubbo/com.coderitl.dubbo.service.FirstService/providers/dubbo%3A%2F%2F10.40.0.19%3A20880%2Fcom.coderitl.dubbo.service.FirstService%3Fanyhost%3Dtrue%26application%3Ddubbo-provider-8080%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dcom.coderitl.dubbo.service.FirstService%26methods%3DsayHello%26pid%3D15532%26release%3D2.7.7%26side%3Dprovider%26timestamp%3D1660132553394

        %3A: => :
        %2F: => /
        %3F: => ?
        %3D: => =
        %26: => &

        # 解析后的内容

        /dubbo/com.coderitl.dubbo.service.FirstService/providers/dubbo://10.40.0.19:20880/com.coderitl.dubbo.service.FirstService?anyhost=true&application=dubbo-provider-8080&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.coderitl.dubbo.service.FirstService&methods=sayHello&pid=15532&release=2.7.7&side=provider&timestamp=1660132553394


      • 案例实现

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        // 第一个 Dubbo 服务提供者: provider
        // RPC 的服务逻辑
        // Dubbo 框架提供了自定义注解,用于服务的注册和发现
        // 当开发的代码是服务提供者的时候,需要把服务注册到注册中心,等待客户端调用
        // 当开发的代码是服务消费者时,需要从注册中心发现服务,并创建代理对象,访问远程服务

        // Dubbo 自定义了注解: @Service 用于注册服务,注意导入的包不要和 spring 的 service 注解混淆

        // Dubbo 2.7.7 版本开始,@Service注解过时,提供新注解@DubboService 共能和 Dubbo 的 @Service 注解完全一致

        package com.coderitl.dubbo.service.impl;

        // @DubboService: 功能是: 在 Spring 容器启动的时候,创建 bean 对象,并拼接一个 URL 保存到指定的注册中心(zookeeper内容解读部分)
        // 默认环境中,Dubbo 的 Provider 是不分组的,可以指定服务分组,目的: 多客户端并行生效,多服务分组管理 group="分组名" =>,@DubboService(loadbalance = "roundrobin",group="coderitl")

        // 当服务端将 Provider 分组后,消费端引用的 Consumer 必须指定要定位的分组(服务端和消费端需同时指定)
        // 默认环境中 Dubbo 的 Provider 没有版本设定的,可以指定版本,目的是: 多版本并行提供服务 商业开发中,服务都需要版本
        // loadbalance = "roundrobin" 负载均衡配置 => 轮询
        @DubboService(loadbalance = "roundrobin")
        public class FirstServiceImpl implements FirstService {
        @Override
        public String sayHello() {
        return "SpringBoot Dubbo sayHello...............";
        }
        }

        1
        2
        3
        4
        5
        6
        7
        8
        // 启动类
        @EnableDubbo
        @SpringBootApplication
        public class DubboProviderMain8080 {
        public static void main(String[] args) {
        SpringApplication.run(DubboProviderMain8080.class, args);
        }
        }
  • consumer 模块

    • 配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      server:
      port: 8001

      dubbo:
      registry:
      address: zookeeper://43.142.80.9:2181
      timeout: 600000
      application:
      name: dubbo-consumer-8001
      protocol:
      name: dubbo
      port: 20880
      config-center:
      timeout: 600000
    • 依赖

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <!-- 在 provider 的基础上添加如下依赖 -->
      <!-- 接口抽取 -->
      <dependency>
      <groupId>com.coderitl.dubbo</groupId>
      <artifactId>dubbo-common-api</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      </dependency>
      <!-- springmvc web 起步依赖 -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
    • 本地服务与远程服务无关

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      package com.coderitl.dubbo.service.impl;

      import com.coderitl.dubbo.service.FirstClientService;
      import com.coderitl.dubbo.service.FirstService;
      import org.apache.dubbo.config.annotation.DubboReference;
      import org.springframework.stereotype.Service;

      // 客户端本地服务实现,和服务端代码实现无关系
      @Service
      public class FirstClientServiceImpl implements FirstClientService {
      // 定义一个服务接口调用 基于 Dubbo 为这个接口创建代理对象
      // 在 2.7.7 以前版本,使用 @Reference 注解, 2.7.7使用 @DubboReference 注解
      // 注解的功能: 就是访问 zookeeper,获取 URI 统一资源路径,并创建代理对象

      // 也可以指定负载均衡策略 loadbalance = "roundrobin"
      // @DubboReference(loadbalance = "roundrobin")
      @DubboReference
      private FirstService firstService;

      @Override
      public String clientSayHello() {
      /**
      * 当前服务代码: 本地无具体逻辑,实现由远程服务提供,需要远程过程调用,RPC 调用
      * 使用 Dubbo 技术,创建一个本地的代理对象,调用远程的服务逻辑,
      * 需要使用一个服务的标准,基于标准(接口),创建代理对象,并调用远程服务
      */
      return firstService.sayHello();
      }
      }

    • 控制器

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      package com.coderitl.dubbo.controller;

      import com.coderitl.dubbo.service.FirstClientService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      @RestController
      public class FirstController {
      /**
      * 如果没有本地的服务逻辑,直接注入远程的服务代理对象也可以(不推荐)
      * 使用 @DubboReference 注解
      * 原因:
      * 因为服务的调用者,一般都会有本地逻辑,直接使用远程服务,可能破坏分层体系
      */
      @Autowired
      private FirstClientService firstClientService;

      @RequestMapping("/first")
      public String first() {
      // 本地服务调用(本地服务通过远程调用)
      return firstClientService.clientSayHello();
      }
      }

    • 访问测试

      访问测试
    • 总结(Dubbo 本身就是一个工具,作用有限,核心是)

      • 把服务端provider 注册到注册中心中去,URL 存储到zookeeper
      • 访问注册中心,获取URL 地址,并创建代理对象

负载均衡

  • 集群: 一个内容,部署多次,形成的整体称为集群,集群中每个个体应该部署到不同的服务器上

  • 伪集群: 集群中内容部署到同一台服务器上,通过不同端口区分不同个体

    负载均衡是在集群前提下,当访问整个集群时,集群中每个节点被访问次数或频率的规则

    Dubbo 内置了四个负载均衡策略,默认为Random

  • 负载均衡策略的决定规则

    • Provider 方默认是负载均衡策略是Random,随机策略
    • Consumner 方默认负载均衡不设定,使用Provider 提供的负载均衡策略
    • 如果双方都设置了负载均衡策略,使用Consumer 的负载均衡策略,因为局部优先,Provider 是给所有的Consumer 提供服务的,负载均衡策略也是给所有的Consumer 提供的默认策略,Consumer 的负载均衡策略只局限在当前i消费者,是一个局部策略
  • 内置策略

    1. Random:随机,随机访问集群中节点, 访问概率和权重有关

    2. RounodRobin: 轮询(推荐,因为可以配置权重), 访问频率和权重有关

      • 权重: 占有比例,集群中每个项目部署的服务器的性能可能是不同的,性能好的服务器权重应该高一些
      weight => random
    3. LeastActice: 活跃数相同的随机,不同的活跃数高的放前面

      最少的活跃调用数
    4. ConsistentHash:一致性Hash,相同参数请求总是发到一个提供者

      一致性Hash,相同参数请求总是发到一个提供者
  • 总结: 在Dubbo 中,负载均衡的配置有3 个位置

    1. @Service | @DubboService 注解上的属性,loadbalance 代表为Provider 集群定义负载均衡策略,默认是random

    2. @Reference | @DubboReference 注解上的属性,loadbalance 代表为Consumer 定义调用Provider 集群时使用的负载均衡策略,默认,采用Provider 提供的负载均衡策略

    3. 在配置文件中,配置文件是当前应用全局配置,注解是当前类型的对象对应的Provider Consumer 配置

      1
      2
      3
      4
      5
      6
      # 配置文件配置负载均衡
      dubbo:
      provider:
      loadbalance: random # 配置当前应用中所有的`Provider`默认的负载均衡,如果注解定义其他负载均衡策略,局部优先
      consumer:
      loadbalance: random # 配置当前应用中所有的 Consumer 的默认负载均衡策略
  • payload

    1
    2
    3
    4
    5
    6
    7
    8
    # 配置文件常用配置: payload
    # payload 是指请求容量和响应容量的限制,默认限制为 8M,8388608 字节 也就是 请求头+请求体容量必须小于 8M,响应头 + 响应体容量必须小于 8M 只能在配置文件中进行配置,只要配置了,请求和响应的容量同步变更

    # 在服务端配置文件中配置
    dubbo:
    provider:
    payload: 83886080

  • retries

    1
    2
    3
    4
    # 幂等性操作,可以配置 > 0,在非幂等性操作 配置为 0 => Consumer 配置中进行配置
    dubbo:
    consumer:
    retries: 2 # 重试次数,默认为 2,Provider因网络问题,无响应,消费端自动重新请求