Dubbo3基础使用

2023-09-17 16:05:23

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

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

在这里插入图片描述

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

优点:

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

缺点:

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

1.2.2 集群

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

在这里插入图片描述

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

1.2.3 垂直拆分

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

在这里插入图片描述

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

优点:

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

缺点:

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

1.2.4 分布式服务

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

优点:

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

缺点:

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

1.2.5 服务治理(SOA)

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

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

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

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

1.2.6 微服务化

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

在这里插入图片描述

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

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

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

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

在这里插入图片描述

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

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

1.4.2 Dubbo架构

在这里插入图片描述
在这里插入图片描述

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

  • 解压
    在这里插入图片描述

  • 修改zk的配置文件

    进入conf,将文件zoo_sample.cfg 改为zoo.cfg
    在这里插入图片描述

  • 测试zk

    启动zookeeper

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

在这里插入图片描述

上面的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.msb</groupId>
    <artifactId>msb-dubbo-ann</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>msb-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.msb.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.5 重构创建公共模块

dubbo-common 存放IUserService 和User

提供端和消费端

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

2.3.6 测试

在这里插入图片描述

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

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

3、Tripe协议

3.1 HTTP1.x协议

在这里插入图片描述

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

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

在这里插入图片描述

4.2 更改配置

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

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

在这里插入图片描述

设置zk地址

在这里插入图片描述

登录时候用户名秘密
在这里插入图片描述

更改端口
在这里插入图片描述

4.3 进行编译

mvn clean package

在这里插入图片描述

找到编译后到jar包:

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

4.4 启动

jar -jar xxx.jar

4.5 访问

localhost:10010

在这里插入图片描述

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 启动检查

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

5.3 超时时间

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

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

  • 在服务提供者添加如下配置:
    在这里插入图片描述

  • 返回结果
    在这里插入图片描述

  • 配置原则

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

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

5.4 重试次数

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

  • 在provider提供方配置
    在这里插入图片描述

  • 在consume配置
    在这里插入图片描述

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

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

备注: 重试是在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.msb.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.msb.common.service.IUserService" timeout="200" protocol="dubbo">
            <dubbo:method name="getUserById" retries="3"/>
            <dubbo:method name="getUserInfoById" retries="1"/>
        </dubbo:reference>
    
    </beans>
    
  • 这里用Autowired注入

在这里插入图片描述

6、过滤器

6.1 Apache Dubbo Filter介绍

  • Apache Dubbo的Filter与Servlet的Filter功能类似

  • 自定义Filter需要扩展Filter接口

  • 自定义Filter可以通过@Activate注解完成默认开启
    在这里插入图片描述

  • 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

创建目录
在这里插入图片描述

然后在里面创建接口文件
在这里插入图片描述

文件内容:对应实现类的全路径
在这里插入图片描述

6.4 访问

在这里插入图片描述

7、上下文参数传递

7.1 使用场景

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

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

7.2 实战使用

客户端代码:

package com.msb.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;
   }
}

客户端配置

在这里插入图片描述

服务端代码

package com.msb.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;
   }

}

服务端配置

在这里插入图片描述

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.msb</groupId>
    <artifactId>msb-dubbo-xml</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>msb-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.msb.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.msb.provider.service.impl.HelloServiceImpl"/>

8.3 创建Consumer

创建工程引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.msb</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.msb.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

更多推荐

http客户端Feign使用

一、RestTemplate方式调用存在的问题先来看我们以前利用RestTemplate发起远程调用的代码:Stringurl="http://userservice/user/"+order.getUserId();Useruser=restTemplate.getForObject(url,User.class);

win系统环境搭建(七)——使用Nginx部署前后端分离项目

windows环境搭建专栏🔗点击跳转win系统环境搭建(七)——使用Nginx部署前后端分离项目本系列windows环境搭建开始讲解如何给win系统搭建环境,本人所用系统是腾讯云服务器的WindowsServer2022,你可以理解成就是你用的windows10系统。我会尽量从Linux的视角去操纵win系统,以达到

Redis 高可用之持久化

目录Redis高可用Redis持久化RDB持久化触发条件执行流程启动时加载修改配置文件AOF持久化配置执行流程命令追加(append)文件写入(write)和文件同步(sync)文件重写(rewrite)文件重写的流程RDB和AOF的优缺点Redis高可用在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准

SpringMVC之JSR303和拦截器

认识JSR303JSR303是一项Java标准规范,也叫做BeanValidation规范,提供了一种JavaBean数据验证的规范方式。在SpringMVC中,可以通过引入JSR303相关的依赖,来实现数据的校验。在使用JSR303进行校验时,需要在需要校验的JavaBean的属性上添加相应的注解,如@NotNull

线上论坛之单元测试

对线上论坛进行单元测试的测试报告源码地址:https://gitee.com/coisini-thirty-three/forum一、用户部分(UserServiceImplTest)1.创建普通用户测试名称createNormalUser()测试源码@TestvoidcreateNormalUser(){//构造用户

C++:AVL树

目录​​​​​​​一、关于AVL树二、AVL树的注意事项1、平衡因子的更新规则:2、旋转的处理①、右右:左单旋②、左左:右单旋③、左右:先左单旋再右单旋④、右左:先右单旋再左单旋三、AVL树模拟实现一、关于AVL树前面学过二叉搜索树,数据在有序或是接近有序时,二叉搜索树效率就非常低了,因此这里引入了AVL树,又叫高度平

链表oj题 && 链表与LinkedList && 栈的概念 && 队列的概念 && 树和二叉树

第1题(编程题)题目名称:求环的入口点题目内容:求环的入口点https://leetcode-cn.com/problems/linked-list-cycle-ii/description/第2题(编程题)题目名称:判断链表带环题目内容:判断链表带环https://leetcode-cn.com/problems/l

软件设计中常见的设计模式

以下是常见的设计模式,并且给出了应用场景:工厂模式(FactoryPattern):用于创建对象,隐藏了具体对象的创建细节,客户端只需要通过工厂接口获取对象即可。应用场景包括:当需要根据不同的参数生成不同类型的对象时;当需要遵循“开闭原则”,即增加新类型时,只需要添加新的工厂类。单例模式(SingletonPatter

Kafka 常见问题

文章目录kafka如何确保消息的可靠性传输Kafka高性能的体现利用Partition实现并行处理利用PageCache如何提高Kafka性能调整内核参数来优化IO性能减少网络开销批处理数据压缩降低网络负载高效的序列化方式kafka如何确保消息的可靠性传输消费端弄丢了数据唯一可能导致消费者弄丢数据的情况,就是消费到了这

java中零拷贝和深拷贝的原理以及实现探究

深拷贝和零拷贝是两个在Java中广泛使用的概念,它们分别用于对象复制和数据传输优化。下面将详细介绍这两个概念的原理,并给出相应的Java代码示例。深拷贝深拷贝(DeepCopy)原理:深拷贝是创建一个对象的完全独立副本,包括对象本身、引用类型的属性和子对象。可以通过序列化和反序列化来实现深拷贝。首先,需要确保要拷贝的对

【2023,学点儿新Java-47】常见字符集介绍:ASCII码、 ISO-8859-1字符集、GBxxx字符集、Unicode码的缺陷、UTF-8 | 补充:条件运算符的练习

前情提要:【2023,学点儿新Java-46】条件运算符:语法格式及示例;基础练习:获取两个数/三个数中的较大值;星期运算|附:测试代码位运算符的使用|运算符优先级【2023,学点儿新Java-45】位运算符:基本语法(左移<<、右移>>、无符号右移>>>、按位与&、按位或|、按位异或^、按位取反~)|补充练习:逻辑运

热文推荐