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 单体架构

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

image.png

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

优点:

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

缺点:

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

1.2.2 集群

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

image.png

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

1.2.3 垂直拆分

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

image.png

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

优点:

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

缺点:

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

1.2.4 分布式服务

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

image.png

优点:

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

缺点:

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

1.2.5 服务治理(SOA)

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

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

image.png

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

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

1.2.6 微服务化

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

image.png

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

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

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

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. 客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次

image.png

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

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

1.4.2 Dubbo架构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image.png

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

  • 解压

    image.png

  • 修改zk的配置文件

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

    image.png

  • 测试zk

    启动zookeeper

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

    image.png

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

2.3 创建工程

2.3.1 创建父工程

mdb-dubbo-ann

父工程控制版本:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.11</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modules>
        <module>dubbo-consumer</module>
        <module>dubbo-provider</module>
        <module>dubbo-common</module>
    </modules>
    <groupId>com.michstabe</groupId>
    <artifactId>michstabe-dubbo-ann</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>michstabe-dubbo-ann</name>
    <packaging>pom</packaging>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <dubbo-version>3.1.8</dubbo-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-rpc-dubbo</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-registry-zookeeper</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

2.3.2 创建提供者

dubbo-provider

引入依赖:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>

增加配置

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 创建客户端

引入依赖

<!--这里是dubbo和SpringBoot桥梁的整合-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--每一种协议都会对应的一个jar比方:dubbo、rest、tripe 三种协议-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<!--注册中心可以是zk,nacos -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>

更改配置

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

image.png

2.3.5 重构创建公共模块

dubbo-common 存放IUserService 和User

提供端和消费端

<dependency>
    <groupId>com.michstabe</groupId>
    <artifactId>dubbo-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

2.3.6 测试

http://localhost:8001/createOrder/232

image.png

2.4 开启rest协议

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

2.4.1 增加依赖

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-rest</artifactId>
</dependency>

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

image.png

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 里面打个端点,看是否进入

image.png

3、Tripe协议

3.1 HTTP1.x协议

image.png

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协议

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 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 父工程引入依赖版本

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

3.4.2 服务提供者引入依赖

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-triple</artifactId>
</dependency>

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 消费方

引入依赖

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-triple</artifactId>
</dependency>

调用处更改协议

@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

image.png

3.5 Triple Streaming

3.5.1 使用场景

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

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

3.5.2 代码实战

  • common中引入jar

    父工程:

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

    common中

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-common</artifactId>
    </dependency>
    
  • 更改服务端代码

    @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下载

image.png

4.2 更改配置

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

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

image.png

设置zk地址

image.png

登录时候用户名秘密

image.png

更改端口

image.png

4.3 进行编译

mvn clean package

image.png

找到编译后到jar包:

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

4.4 启动

jar -jar xxx.jar

4.5 访问

localhost:10010

image.png

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 超时时间

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

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

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

    image.png

  • 返回结果

    image.png

  • 配置原则

    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); // 有具体实现类
     }
    
    <?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"
           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">
    
    
        <!-- 调用远程Producer的服务 -->
        <dubbo:reference id="iUserService"  interface="com.michstabe.common.service.IUserService" timeout="200" protocol="dubbo">
            <dubbo:method name="getUserById" retries="3"/>
            <dubbo:method name="getUserInfoById" retries="1"/>
        </dubbo:reference>
    
    </beans>
    
    <?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"
           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">
    
    
        <!-- 调用远程Producer的服务 -->
        <dubbo:reference id="iUserService"  interface="com.michstabe.common.service.IUserService" timeout="200" protocol="dubbo">
            <dubbo:method name="getUserById" retries="3"/>
            <dubbo:method name="getUserInfoById" retries="1"/>
        </dubbo:reference>
    
    </beans>
    
  • 这里用Autowired注入

    image.png

6、过滤器

6.1 Apache Dubbo Filter介绍

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

image.png

  • 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

创建目录

image.png

然后在里面创建接口文件

image.png

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

image.png

6.4 访问

image.png

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;
   }
}

客户端配置

image.png

服务端代码

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;
   }

}

服务端配置

image.png

8、Dubbo 工程XML配置

8.1 创建父工程

更改pom

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>dubbo-provider</module>
        <module>dubbo-consumer</module>
        <module>dubbo-common</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.michstabe</groupId>
    <artifactId>michstabe-dubbo-xml</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>michstabe-dubbo-xml</name>
    <packaging>pom</packaging>
    <description>dubbo学习实例</description>
    <properties>
        <java.version>1.8</java.version>
        <dubbo-version>3.1.8</dubbo-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-rpc-dubbo</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-rpc-rest</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-rpc-triple</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-common</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-registry-zookeeper</artifactId>
                <version>${dubbo-version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

8.2 创建Provider

引入依赖

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

application.yml配置

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

applicationContext-dubbo.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"
       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">

    <!-- 提供方应用信息,用于计算依赖关系,name可以随便起名,但是不能重复 -->
    <dubbo:application name="dubbo-provider"/>

    <!-- 使用zookeeper为注册中心,客户端使用curator -->
    <dubbo:registry address="zookeeper://localhost:2181" client="curator"/>
    <!--对外提供服务helloServiceAPI,服务对应的实现类是ref="HelloServiceImpl"-->

    <dubbo:protocol name="dubbo" port="20885"/>
    <dubbo:service id="helloServiceAPI"
                   interface="com.michstabe.service.HelloServiceAPI"
                   ref="HelloServiceImpl" retries="2">
    </dubbo:service>

   

</beans>

主类配置

@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

 <bean id="HelloServiceImpl" class="com.michstabe.provider.service.impl.HelloServiceImpl"/>

8.3 创建Consumer

创建工程引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.michstabe</groupId>
    <artifactId>dubbo-common</artifactId>
    <version>0.0.2-SNAPSHOT</version>
</dependency>
<!--这里是dubbo和SpringBoot桥梁的整合-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--每一种协议都会对应的一个jar比方:dubbo、rest、tripe 三种协议-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<!--注册中心可以是zk,nacos -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

application.yml配置

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

applicationContext-dubbo.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"
       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">

    <!-- 提供方应用信息,用于计算依赖关系,name可以随便起名,但是不能重复 -->
    <dubbo:application name="dubbo-consumer" />

    <!-- 使用zookeeper为注册中心,客户端使用curator -->
    <dubbo:registry address="zookeeper://localhost:2181" client="curator"/>

    <!-- 调用远程Producer的服务 -->
    <dubbo:reference id="helloServiceAPI" timeout="200" interface="com.michstabe.service.HelloServiceAPI" retries="3"/>

</beans>

主类创建

@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

image.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值