Dubbo3深入实战

1、Dubbo概述

现在我们SpringCloud Alibaba比较火,用的比较多是吧,那dubbo是不是过时的呢? 不是啊,以前有人把Dubbo和SpringCloud进行对比,其实两者是不同维度的,不能对比,dubbo就是一个rpc框架,SpringCloud是一个生态,里面包括很多组件,并且dubbo3也可以和SpringCloudAlibaba一些组件进行整合,并且我认为dubbo和SpringCloud alibaba进行整合后能发发挥两个的优势,dubbo的rpc调用的性能会很好,SpringCloudAlibaba服务治理能力很强。微服务中我们经常使用feign,feign是发送http请求而dubbo是rpc,性能要好我们可以dubbo代替feign的http请求是吧。并且他作为一个老牌子的分布式框架用的人还是比较多

1.1 什么是分布式系统?

  • 《分布式系统原理与范型》定义:

    • “分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统”
    • 分布式系统(distributed system)是建立在网络之上的软件系统。
    • 简单来说:多个(不同职责)人共同来完成一件事!
    • 任何一台服务器都无法满足淘宝的双十一的数据吞吐量,一定是很多台服务器公共来完成的。
  • 歇后语:“三个臭皮匠赛过诸葛亮”,就是分布式系统的真实写照

1.2 服务架构的演

1.2.1 单体架构

单体架构应该是我们最先接触到的架构实现了,在单体架构中使用经典的三层模型,即表现层,业务逻辑层和数据访问层。

Dubbo3深入实战_第1张图片

单体架构只适合在应用初期,且访问量比较低的情况下使用,

优点:

  • 性价比很高
  • 开发速度快
  • 成本低

缺点:

  • 代码耦合,开发维护困难
  • 无法针对不同模块进行针对性优化
  • 无法水平扩展
  • 单点容错率低,并发能力差

1.2.2 集群

针对单个服务器在访问量越来越大的情况越来越吃力的情况,我们可以考虑服务器的集群话处理。

Dubbo3深入实战_第2张图片

集群的部署大大提高了服务的处理能力,同时利用Nginx提供的负载均衡机制,来分发请求,使用户的体验没有改变。

1.2.3 垂直拆分

上面的集群部署是可以解决一部分的服务器压力,但是随着用户访问量的增多,集群节点增加到一定阶段的时候,其实作用就已经不是太大了,因为将所有的业务都集中在一起,造成耦合度很高,这时我们可以考虑业务的拆分。来提高系统的性能。比如将原来在一个系统里面的业务拆分为用户系统,订单系统和商品系统。也就是我们讲的垂直化拆分如下:

Dubbo3深入实战_第3张图片

服务垂直化拆分后是可以大大的提高整体的服务处理能力,但是也会出现很多的冗余的代码,比如用户系统要操作订单库,要操作商品库,订单系统也有可能要操作用户库和商品库等。

优点:

  • 系统拆分实现了流量分担,解决了并发问题
  • 可以针对不同模块进行优化
  • 方便水平扩展,负载均衡,容错率提高

缺点:

  • 系统间相互独立,会有很多重复开发工作,影响开发效率

1.2.4 分布式服务

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式调用是关键。

Dubbo3深入实战_第4张图片

优点:

  • 将基础服务进行了抽取,系统间相互调用,提高了代码复用和开发效率

缺点:

  • 系统间耦合度变高,调用关系错综复杂,难以维护

1.2.5 服务治理(SOA)

针对垂直化拆分出现的问题,这时就出现了我们经常听到的SOA(面向服务的架构).什么是SOA呢?在《微服务设计》中有这么一段描述

SOA是一种设计方法,其中包括多个服务,而服务之间通过配合最终会提供一系列功能,一个服务通常以独立的形式存在于操作系统进程中,服务之间通过网络调用,而非采用进程内调用的方式进行通信。

Dubbo3深入实战_第5张图片

ESB:简单来说ESB就是一根管道,用来连接各个服务节点,为了集成不同的系统,不同协议的服务,ESB要实现消息的转发解释和路由的功能,让不同的服务进行互通

  • 缺点:
    • 每个供应商提供的ESB产品本身有偏差,自身实现起来比较复杂;应用服务多了之后,ESB要去集成所有服务的协议,数据转换和运维部署将变的比较困难,所有服务都是通过一根管道进行通信,直接降低了通信速度。

1.2.6 微服务化

微服务架构就是使用一套小服务来开发单体应用的方式或途径,每个服务都是基于单一业务能力构建,运行在自己的进程中,并且使用轻量级的机制进行通信,通常就是HTTP或者RPC,能够通过自动化部署机制来自动部署环境。这些服务可以使用不同的编程语言,不同的数据库存储 技术,并保持最低限度的集中式管理。

Dubbo3深入实战_第6张图片

运维困难: 我们这二三十个服务,二三是个服务对应二三是个服务这样部署

分布式事务: 商品、交易的等等这些调用会出现调用失败,如果失败就需要回滚,这个是不是就设计到分布式事务了,本地事务就解决不了问题

定位问题:我们需要定位那个服务出现,并且需要定位具体哪服务的那台机器出现问题

1.3 Dubbo简介

1.3.1 常见的微服务框架

  • Spring Cloud、Spring Cloud Alibaba
  • Dubbo
  • Thrift (社区活跃度不是很好)

1.3.2 Dubbo是什么

  • Dubbo是分布式服务框架,是阿里巴巴的开源项目,现交给apache进行维护
  • Dubbo致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
  • 简单来说,dubbo是个服务框架,如果没有分布式的需求,是不需要用的、

1.3.3 里程碑

里程碑 里程碑内容
2011年10月 阿里巴巴完成Dubbo开源
2012年到2014年 Dubbo开源社区蓬勃发展,多个互联网大厂进行封装使用课
2014年10月 Dubbo停止更新
2017年9月 阿里巴巴重启Dubbo的维护和更新
2018年2月 Dubbo进入Apache顶级孵化项目
2021年3月 Dubbo 3.x正式进入发布倒计时

1.4 Dubbo整体架构

1.4.1 RPC

  • RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式
  • RPC基本的通信原理
  1. 在客户端将对象进行序列化
  2. 底层通信框架使用netty(基于tcp协议的socket),将序列化的对象发给服务方提供方
  3. 服务提供方通过socket得到数据文件之后,进行反序列化,获得要操作的对象
  4. 对象数据操作完毕,将新的对象序列化,再通过服务提供方的socket返回给客户端
  5. 客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次

Dubbo3深入实战_第7张图片

RPC两个核心模块:通讯(socket),序列化

节点 角色说明
Provider 服务的提供方
Consumer 服务的消费方
Registry 服务注册与发现的注册中心
Monitor 监控服务的统计中心
Container 服务运行容器

1.4.2 Dubbo架构

Dubbo3深入实战_第8张图片

Dubbo3深入实战_第9张图片

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

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

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

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

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

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

2、构建Dubbo工程

2.1 框架依赖

  • Maven
  • SpringBoot 2.6.11
  • Dubbo 3.1.8 + zookeeper 3.4.14

2.2 搭建Zookeeper

  • 解压

    Dubbo3深入实战_第10张图片

  • 修改zk的配置文件

    进入conf,将文件zoo_sample.cfg 改为zoo.cfg

    Dubbo3深入实战_第11张图片

  • 测试zk

    启动zookeeper

    执行zookeeper根目录下,bin文件中的zkServer.cmd

    Dubbo3深入实战_第12张图片

    上面的CMD窗口不要关闭,这样zookeeper就是出于运行状态了

2.3 创建工程

2.3.1 创建父工程

mdb-dubbo-ann

父工程控制版本:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.6.11
         
    
    
        dubbo-consumer
        dubbo-provider
        dubbo-common
    
    com.michstabe
    michstabe-dubbo-ann
    0.0.1-SNAPSHOT
    michstabe-dubbo-ann
    pom
    Demo project for Spring Boot

    
        1.8
        3.1.8
    
    
        
            org.projectlombok
            lombok
        
    

    
        
            
                org.apache.dubbo
                dubbo-spring-boot-starter
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-rpc-dubbo
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-registry-zookeeper
                ${dubbo-version}
            
        
    



2.3.2 创建提供者

dubbo-provider

引入依赖:


    org.apache.dubbo
    dubbo-spring-boot-starter


    org.apache.dubbo
    dubbo-rpc-dubbo


    org.apache.dubbo
    dubbo-registry-zookeeper

增加配置

server:
  port: 8002
logging:
  config: classpath:logback.xml
dubbo:
  application:
    name: dubbo-provider
  protocol:
    name: dubbo
    #客户端链接20880就可以访问我们的dubbo
    port: 20883
  registry:
    address: zookeeper://127.0.0.1:2181

更改主类

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
// 因为是自动装配也可以不加这个注解
@EnableDubbo(scanBasePackages = "com.michstabe.dubbo.provider.service")
@SpringBootApplication
public class DubboProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class);
    }
}

接着增加通信端口

public interface IUserService {
    User getUserById(Long id);
}
@Data
@AllArgsConstructor
@Builder
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long id;
    private String name;
    private int age;

}

增加实现类

@DubboService// 定义一个dubbo服务
public class UserServiceImpl implements IUserService {
    @Override
    public User getUserById(Long id) {
        User user = User.builder().id(id)
                .age(12)
                .name("天涯")
                .build();
        return user;
    }

2.3.3 创建客户端

引入依赖



    org.apache.dubbo
    dubbo-spring-boot-starter



    org.apache.dubbo
    dubbo-rpc-dubbo



    org.apache.dubbo
    dubbo-registry-zookeeper

更改配置

server:
  port: 8001
logging:
  config: classpath:logback.xml
dubbo:
  application:
    name: dubbo-consumer
  registry:
    address: zookeeper://127.0.0.1:2181

更改主类

@EnableDubbo
@SpringBootApplication
public class DubboConsumeApplication {
    public static void main(String[] args) {
        SpringApplication.run(DubboConsumeApplication.class);
    }
}

增加调用接口

public interface IUserService {
    User getUserById(Long id);
}
@Data
@AllArgsConstructor
@Builder
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long id;
    private String name;
    private int age;

}

增加业务调用处理

@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @RequestMapping("/createOrder/{userId}")
    public String createOrder(@PathVariable("userId") Long userId){
        return orderService.createOrder(userId);
    }
}
@Slf4j
@Service
public class OrderService {
    // 引用对应的dubbo服务
    @DubboReference
    private IUserService iUserService;
    public String createOrder(Long userId){
        User user = iUserService.getUserById(userId);
        log.info("用户用户信息:{}",user);
        return "创建订单成功";
    }
}

2.3.4 测试

http://localhost:8001/createOrder/232

Dubbo3深入实战_第13张图片

2.3.5 重构创建公共模块

dubbo-common 存放IUserService 和User

提供端和消费端


    com.michstabe
    dubbo-common
    0.0.1-SNAPSHOT

2.3.6 测试

http://localhost:8001/createOrder/232

Dubbo3深入实战_第14张图片

2.4 开启rest协议

如果我们的服务希望既要支持dubbo协议调用,也要能支持http调用,所以,要么仍然保留SpringMVC那一套,如果不想保留那一套,就可以开启 dubbo中的rest协议。

2.4.1 增加依赖


    org.apache.dubbo
    dubbo-rpc-rest

2.4.2 更改配置

dubbo:
  application:
    name: dubbo-provider
  # 这里的协议加了s,所以可以设置多个通信协议
  protocols:
    p1:
      name: dubbo
      #客户端链接20883就可以访问我们的dubbo
      port: 20883
    p2:
      name: rest
      #客户端链接20884就可以访问我们的rest
      port: 20884

2.4.3 更改对应代码服务

@DubboService// 定义一个dubbo服务
@Path("/user")
public class UserServiceImpl implements IUserService {
    @GET
    @Path("/{userId}")
    @Produces(MediaType.APPLICATION_JSON)
    @Override
    public User getUserById(@PathParam("userId") Long userId) {
        User user = User.builder().id(userId)
                .age(12)
                .name("天涯")
                .build();
        return user;
    }

}

2.4.4 测试

在消费端增加RestTemplate

@EnableDubbo
@SpringBootApplication
public class DubboConsumeApplication {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(DubboConsumeApplication.class);
    }
}
@Slf4j
@Service
public class OrderService {
    @DubboReference
    private IUserService iUserService;

    @Autowired
    RestTemplate restTemplate;
    public String createOrder(Long userId){
        User user = restTemplate.getForObject("http://localhost:20884/user/232",User.class);
        log.info("用户用户信息:{}",user);
        return "创建订单成功";
    }
}

http://localhost:8001/createOrder/232

Dubbo3深入实战_第15张图片

2.4.5 使用接口调用Rest

将rest协议放到common中

修改IUserService

@Path("/user")
public interface IUserService {
    @GET
    @Path("/{userId}")
    @Produces(MediaType.APPLICATION_JSON)
    User getUserById(@PathParam("userId") Long id);
}

修改consume里面内容

@Slf4j
@Service
public class OrderService {
    // 指定写协议
    @DubboReference(protocol = "rest")
    private IUserService iUserService;

    @Autowired
    RestTemplate restTemplate;
    public String createOrder(Long userId){
        User user = iUserService.getUserById(userId);
        log.info("用户用户信息:{}",user);
        return "创建订单成功";
    }
}

如果我们不能确定是否是走的http,我们可以DispatcherServlet#service 里面打个端点,看是否进入

Dubbo3深入实战_第16张图片

3、Tripe协议

3.1 HTTP1.x协议

Dubbo3深入实战_第17张图片

POST /user HTTP/1.1                       // 请求行
Host: www.user.com
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
User-agent: Mozilla/5.0.                  // 以上是请求头
(此处必须有一空行 |                         // 空行分割header和请求内容 
name=world                                // 请求体(可选,如get请求时可选)

它会将上面字符转化为字节流,然后发送给对方,对方解析的时候会根据空格、回车、换行符进行解析

这里我们可以知道他的请求头比较大,占用空间比较多。

3.2 Dubbo协议

Dubbo3深入实战_第18张图片

  • Magic - Magic High & Magic Low (16 bits)

    标识协议版本号,Dubbo 协议:0xdabb

  • Req/Res (1 bit)

    标识是请求或响应。请求: 1; 响应: 0。

  • 2 Way (1 bit)

    仅在 Req/Res 为1(请求)时才有用,标记是否期望从服务器返回值。如果需要来自服务器的返回值,则设置为1。

  • Event (1 bit)

    标识是否是事件消息,例如,心跳事件。如果这是一个事件,则设置为1。

  • Serialization ID (5 bit)

    标识序列化类型:比如 fastjson 的值为6。

  • Status (8 bits)

    仅在 Req/Res 为0(响应)时有用,用于标识响应的状态。

    • 20 - OK
    • 30 - CLIENT_TIMEOUT
    • 31 - SERVER_TIMEOUT
    • 40 - BAD_REQUEST
    • 50 - BAD_RESPONSE
    • 60 - SERVICE_NOT_FOUND
    • 70 - SERVICE_ERROR
    • 80 - SERVER_ERROR
    • 90 - CLIENT_ERROR
    • 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR
  • Request ID (64 bits)

    标识唯一请求。类型为long。

  • Data Length (32 bits)

    序列化后的内容长度(可变部分),按字节计数。int类型。

  • Variable Part

    被特定的序列化类型(由序列化 ID 标识)序列化后,每个部分都是一个 byte [] 或者 byte

dubbo协议在Dubbo框架内使用还是比较舒服的,并且dubbo协议相比于http1.x协议,性能会更好,因为请求中没
有多余的无用的字节,都是必要的字节,所以dubbo协议成为了Dubbo框架中的默认协议。

但是dubbo协议一旦涉及到跨RPC框架,比如一个Dubbo服务要调用gPRC服务,就比较麻烦了,因为发一个dubbo
协议的请求给一个gPRC服务,gPRC服务只会按照gRPC的格式来解析字节流,最终肯定会解析不成功的。

dubbo协议虽好,但是不够通用,所以这就出现了Triple协议,Triple协议是基于HTTP2,没有性能问题,另外HTTP
协议非常通用,全世界都认它,兼容起来也比较简单,而且还有很多额外的功能,比如流式调用。

3.3 Triple 协议

Triple 协议是 Dubbo3 推出的主力协议。Triple 意为第三代,通过 Dubbo1.0/ Dubbo2.0 两代协议的演进,以及云原生带来的技术标准化浪潮,Dubbo3 新协议 Triple 应运而生。

3.4 项目改造

3.4.1 父工程引入依赖版本


    org.apache.dubbo
    dubbo-rpc-triple
    ${dubbo-version}

3.4.2 服务提供者引入依赖


    org.apache.dubbo
    dubbo-rpc-triple

3.4.3 引入协议

dubbo:
  application:
    name: dubbo-provider
  protocols:
    p1:
      name: dubbo
      #客户端链接20880就可以访问我们的dubbo  20880 是默认端口
      port: 20883
    p2:
      name: rest
      #客户端链接20884就可以访问我们的rest
      port: 20884
    p3:
      name: tri
      port: 20885

3.4.4 消费方

引入依赖


    org.apache.dubbo
    dubbo-rpc-triple

调用处更改协议

@Slf4j
@Service
public class OrderService {

    @DubboReference(protocol = "tri")
    private IUserService iUserService;

    @Autowired
    RestTemplate restTemplate;
    public String createOrder(Long userId){
        User user = iUserService.getUserById(userId);
        log.info("用户用户信息:{}",user);
        return "创建订单成功";
    }
}

3.4.5 测试

http://localhost:8001/createOrder/232

Dubbo3深入实战_第19张图片

3.5 Triple Streaming

3.5.1 使用场景

在一些大文件传输、直播等应用场景中, consumer或provider需要跟对端进行大量数据的传输,由于这些情况下的数据量是非常大的,因此是没有办法可以在一个RPC的数据包中进行传输,因此对于这些数据包我们需要对数据包进行分片之后,通过多次RPC调用进行传输,如果我们对这些已经拆分了的RPC数据包进行并行传输,那么到对端后相关的数据包是无序的,需要对接收到的数据进行排序拼接,相关的逻辑会非常复杂。但如果我们对拆分了的RPC数据包进行串行传输,那么对应的网络传输RTT与数据处理的时延会是非常大的。

为了解决以上的问题,并且为了大量数据的传输以流水线方式在consumer与provider之间传输,因此Streaming RPC的模型应运而生。

3.5.2 代码实战

  • common中引入jar

    父工程:

    
        org.apache.dubbo
        dubbo-common
        ${dubbo-version}
    
    

    common中

    
        org.apache.dubbo
        dubbo-common
    
    
  • 更改服务端代码

    @Path("/user")
    public interface IUserService {
        // UNARY
        @GET
        @Path("/{userId}")
        @Produces(MediaType.APPLICATION_JSON)
        User getUserById(@PathParam("userId") Long id);
    
        // SERVER_STREAM  服务端流
        default  void sayHelloServerStream(String name, StreamObserver<String> response){
    
        }
    
        // CLIENT_STREAM / BI_STREAM  双端流
        default StreamObserver<String> sayHelloStream(StreamObserver<String> response){
            return response;
        }
    
    
    
    }
    
    • 服务端流处理

      @DubboService// 定义一个dubbo服务
      public class UserServiceImpl implements IUserService {
          @Override
          public void sayHelloServerStream(String name, StreamObserver<String> response) {
              response.onNext("hello 1");
              response.onNext("hello 2");
              response.onNext("hello 3");
              response.onNext("hello 4");
              response.onCompleted();
      
          }
      }
      
      @Service
      public class OrderService {
      
          @DubboReference(protocol = "tri")
          private IUserService iUserService;
          public String createOrder(Long userId){
              iUserService.sayHelloServerStream("李华", new StreamObserver<String>() {
                  @Override
                  public void onNext(String data) {
                      System.out.println("接收到的响应数据:" + data);
                  }
      
                  @Override
                  public void onError(Throwable throwable) {
                      System.err.println("异常处理");
                  }
      
                  @Override
                  public void onCompleted() {
                      System.out.println("响应数据完成");
                  }
              });
              return "创建订单成功";
          }
      }
      
    • 双端流

      @DubboService// 定义一个dubbo服务
      public class UserServiceImpl implements IUserService {
      
          @Override
          public User getUserById(Long userId) {
              User user = User.builder().id(userId)
                      .age(12)
                      .name("天涯")
                      .build();
              return user;
          }
      
          @Override
          public StreamObserver<String> sayHelloStream(StreamObserver<String> response) {
              return new StreamObserver<String>() {
                  @Override
                  public void onNext(String data) {
                      // 接收到客户端发送过来的数据,进行处理,将结果返回
                      try {
                          Thread.sleep(3*1000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("接收到客户端数据:" + data);
                      response.onNext("服务端影响:" + data);
                  }
      
                  @Override
                  public void onError(Throwable throwable) {
      
                  }
      
                  @Override
                  public void onCompleted() {
                      System.out.println("服务端处理完毕");
                  }
              };
          }
      }
      
public class OrderService {

    @DubboReference(protocol = "tri")
    private IUserService iUserService;

    @Autowired
    RestTemplate restTemplate;
    public String createOrder(Long userId){

        StreamObserver streamObserver = iUserService.sayHelloStream(new StreamObserver() {
            @Override
            public void onNext(Object data) {
                System.out.println("接收到响应数据:" + data);
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onCompleted() {
                System.out.println("接收到响应数据完毕");
            }
        });
        streamObserver.onNext("request hello 1");
        streamObserver.onNext("request hello 2");
        streamObserver.onNext("request hello 3");
        streamObserver.onCompleted();
        return "创建订单成功";
    }
}

4、dubbo-admin

4.1下载

Dubbo3深入实战_第20张图片

4.2 更改配置

进入application.properties配置文件所在的目录

dubbo-admin-0.5.0\dubbo-admin-server\src\main\resources\application.properties

Dubbo3深入实战_第21张图片

设置zk地址

Dubbo3深入实战_第22张图片

登录时候用户名秘密

Dubbo3深入实战_第23张图片

更改端口

Dubbo3深入实战_第24张图片

4.3 进行编译

mvn clean package

Dubbo3深入实战_第25张图片

找到编译后到jar包:

dubbo-admin-0.5.0\dubbo-admin-distribution\target

4.4 启动

jar -jar xxx.jar

4.5 访问

localhost:10010

Dubbo3深入实战_第26张图片

4.6 使用提供jar的注意事项

注册中心: 127.0.0.1:2181
端口号: 10010
用户名/密码: root/root

5、参数设置

5.1 版本和分组

Dubbo服务中,接口并不能唯一确定一个服务,只有接口+分组+版本号才能唯一确定一个服务。

使用场景

  • 当同一个接口针对不同的业务场景、不同的使用需求或者不同的功能模块等场景,可使用服务分组来区分不同的实现方式。同时,这些不同实现所提供的服务是可并存的,也支持互相调用。
  • 当接口实现需要升级又要保留原有实现的情况下,即出现不兼容升级时,我们可以使用不同版本号进行区分。

使用方式

使用 @DubboService 注解,添加 group 参数和 version 参数 本示例中使用"发布和调用" 中示例代码

代码处理

服务端,我们指定组合版本,然后启动两个服务,注意两个服务的端口不能相同

@DubboService(group = "group1",version = "1.0")// 定义一个dubbo服务
public class UserServiceImpl implements IUserService {

    @Override
    public User getUserById(Long userId) {
        User user = User.builder().id(userId)
                .age(12)
                .name("天涯")
                .build();
        log.info("服务获取用户信息:{}",user);
        return user;
    }

修改消费端指定版本

@DubboReference(protocol = "tri",group = "group1",version = "2.0")
private IUserService iUserService;

测试,访问只会达到对应的version=“2.0” 的版本

5.2 启动检查

启动时会在注册中心检查依赖的服务是否可用,不可用时会抛出异常

image.png

5.3 超时时间

  • 由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)

  • 为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间

  • 在服务提供者添加如下配置:

    Dubbo3深入实战_第27张图片

  • 返回结果

    Dubbo3深入实战_第28张图片

  • 配置原则

    dubbo推荐在Provider上尽量多配置Consumer端属性:

    1. 作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
    2. 在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作消费者的缺省值

5.4 重试次数

  • 当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值是2次,我们可以自行设置

  • 在provider提供方配置

    image.png

  • 在consume配置

    image.png

  • 并不是所有的方法都适合设置重试次数

    • 幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
    • 非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)

备注: 重试是在dubbo协议下重试, tri协议下不会生效, consumer和provider同时设置重试则客户端生效

  • 指定不同方法的重试,这是后我们需要引入xml

  • 增加方法

  • public interface IUserService {
        User getUserById(@PathParam("userId") Long id);
    
        User getUserInfoById(@PathParam("userId") Long id); // 有具体实现类
     }
    
    
    
    
    
        
        
            
            
        
    
    
    
    
    
    
    
        
        
            
            
        
    
    
    
  • 这里用Autowired注入

    Dubbo3深入实战_第29张图片

6、过滤器

6.1 Apache Dubbo Filter介绍

  • Apache Dubbo的Filter与Servlet的Filter功能类似
  • 自定义Filter需要扩展Filter接口
  • 自定义Filter可以通过@Activate注解完成默认开启

Dubbo3深入实战_第30张图片

  • filter 分consumer和provider

6.2 自定义Filter

//这里group就是定义提供者还是消费者
@Activate(group = "provider")
public class MyFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        URL url = invoker.getUrl();
        Class<?> anInterface = invoker.getInterface();
        String simpleName = anInterface.getSimpleName();
        String serviceName = invocation.getServiceName();
        String methodName = invocation.getMethodName();

        System.out.println("url = " + url.toFullString());
        System.out.println("simpleName = " + simpleName);
        System.out.println("serviceName = " + serviceName);
        System.out.println("methodName = " + methodName);
        // 调用下一级
        Result result = invoker.invoke(invocation);
        return result;
    }
}

6.3 配置Filter

创建目录

Dubbo3深入实战_第31张图片

然后在里面创建接口文件

Dubbo3深入实战_第32张图片

文件内容:对应实现类的全路径

image.png

6.4 访问

Dubbo3深入实战_第33张图片

7、上下文参数传递

7.1 使用场景

1、Dubbo系统间调用时,想传递一些通用参数,可通过Dubbo提供的扩展如Filter等实现统一的参数传递

2、Dubbo系统间调用时,想传递接口定义之外的参数,可在调用接口前使用setAttachment传递参数。

7.2 实战使用

客户端代码:

package com.michstabe.dubbo.consumer.filters;

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

@Activate(group = "consumer")
public class ConsumerContextFilter implements Filter {
   
   
   @Override
   public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
   
      Result result = null;
      RpcContext.getClientAttachment()
            .setAttachment("xid","1111111111111");
      try {
         //执行业务逻辑
         result = invoker.invoke(invocation);

      }finally {
         //清理
         RpcContext.getClientAttachment().clearAttachments();
      }
      return result;
   }
}

客户端配置

Dubbo3深入实战_第34张图片

服务端代码

package com.michstabe.dubbo.provider.filter;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.Map;

@Slf4j
@Activate(group = "provider")
public class ProviderContextFilter implements Filter {
   
   
   @Override
   public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
       //ServerAttachment接收客户端传递过来的参数
      Map<String, Object> serverAttachments = RpcContext.getServerAttachment().getObjectAttachments();
      log.info("ContextService serverAttachments:" + JSON.toJSONString(serverAttachments));


      String xid = (String)serverAttachments.get("xid");
      log.info("获取传递数据xid:{}",xid);
      //执行业务逻辑
      Result result = invoker.invoke(invocation);

      return result;
   }

}

服务端配置

Dubbo3深入实战_第35张图片

8、Dubbo 工程XML配置

8.1 创建父工程

更改pom



    4.0.0
    
        dubbo-provider
        dubbo-consumer
        dubbo-common
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.5.0
         
    
    com.michstabe
    michstabe-dubbo-xml
    0.0.1-SNAPSHOT
    michstabe-dubbo-xml
    pom
    dubbo学习实例
    
        1.8
        3.1.8
    
    
        
            org.projectlombok
            lombok
        
    
    
        
            
                org.apache.dubbo
                dubbo-spring-boot-starter
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-rpc-dubbo
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-rpc-rest
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-rpc-triple
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-common
                ${dubbo-version}
            
            
                org.apache.dubbo
                dubbo-registry-zookeeper
                ${dubbo-version}
            
        
    


8.2 创建Provider

引入依赖


    org.apache.dubbo
    dubbo-spring-boot-starter


    org.apache.dubbo
    dubbo-rpc-dubbo


    org.apache.dubbo
    dubbo-registry-zookeeper


    org.springframework.boot
    spring-boot-starter

application.yml配置

server:
  port: 8001
logging:
  config: classpath:logback.xml

applicationContext-dubbo.xml




    
    

    
    
    

    
    
    

   


主类配置

@ImportResource(locations = {"classpath:applicationContext-dubbo.xml"})
@SpringBootApplication
public class DubboProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(DubboProviderApplication.class);
    }
}

服务实现

@Slf4j
public class HelloServiceImpl implements HelloServiceAPI {
    @Override
    public String sayHello(String message) {

        log.info("接收到消息:{}",message );
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "生产者接收到数据:" + message;
    }
}

dubbo-common工程以及接口创建

public interface HelloServiceAPI {
    String sayHello(String message);
}

更改applicationContext-dubbo.xml

 

8.3 创建Consumer

创建工程引入依赖


    org.springframework.boot
    spring-boot-starter-web


    com.michstabe
    dubbo-common
    0.0.2-SNAPSHOT



    org.apache.dubbo
    dubbo-spring-boot-starter



    org.apache.dubbo
    dubbo-rpc-dubbo



    org.apache.dubbo
    dubbo-registry-zookeeper


    org.springframework.boot
    spring-boot-starter


    org.springframework.boot
    spring-boot-starter-test
    test

application.yml配置

server:
  port: 8002
logging:
  config: classpath:logback.xml

applicationContext-dubbo.xml配置




    
    

    
    

    
    


主类创建

@ImportResource(locations = {"classpath:applicationContext-dubbo.xml"})
@SpringBootApplication
public class DubboConsumeApplication {
    public static void main(String[] args) {
        SpringApplication.run(DubboConsumeApplication.class);
    }
}

业务逻辑

@RestController
public class HelloController {
    @Autowired
    private HelloServiceAPI helloServiceAPI;
    @GetMapping("/sayHello")
    public String sayHelle(){
        return helloServiceAPI.sayHello("hello everyBody");
    }
}

8.4 测试

http://localhost:8002/sayHello

Dubbo3深入实战_第36张图片

你可能感兴趣的:(RPC框架,rpc)