一篇文章搞懂Dubbo(附dubbo_demo源码)


我们来回顾一下不断演进的服务架构

单体架构 -> 分布式架构 -> 微服务

单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署

image-20210713202807818

优点:

  • 架构简单
  • 部署成本低

缺点:

  • 耦合度高(维护困难、升级困难)

分布式架构:根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务

image-20210713203124797

优点:

  • 降低服务耦合
  • 有利于服务升级和拓展

缺点:

  • 服务调用关系错综复杂

分布式架构虽然降低了服务耦合,但是服务拆分时也有很多问题需要思考:

  • 服务拆分的粒度如何界定?
  • 服务之间如何调用?
  • 服务的调用关系如何管理?

人们需要制定一套行之有效的标准来约束分布式架构,进而有了我们现在的微服务,微服务的上述特性其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。

因此,可以认为微服务是一种经过良好架构设计的分布式架构方案

image-20210713203753373

微服务的架构特征:

  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
  • 自治:团队独立、技术独立、数据独立,独立部署和交付
  • 面向服务:服务提供统一标准的接口,与语言和技术无关
  • 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题

现在国内用的最多的微服务就是SpringCloud,它的核心组件包括:

image-20210713204155887

1. 什么是Dubbo

我们在学习SpringCloud的时候,为了调用其他微服务里面的接口,经常会使用Feign来调用,其实这就是一种远程过程调用(RPC),是基于Http协议的远程服务调用

Dubbo其实也是干类似的事情的,即Dubbo是一款高性能的RPC框架,用来解决微服务中各个模块调用的问题,那我们既然有了Feign,为什么还要有Dubbo呢?那是因为对于微服务模块来说HTTP协议还是太重了,HTTP协议在设计之处为了许多的安全性,设置了许多健壮性的设计,对于微服务模块来说,其实完全可以采用更加轻量级的通讯协议来完成远程服务调用,Dubbo应运而生

Dubbo主要从以下两个方面来加快远程调用的速度,这两个方面同时也是我们进行网络IO最耗时的方面:

  • 序列化

    我们在学习Java网络编程的时候知道,一个对象要想在网络中传输,就必须要实现Serializable接口进行序列化,一般序列化的方式有:xml、json、二进制流等,其中效率最高的就是二进制流(因为网络传输的本质就是通过二进制传输的),Dubbo采用的就是效率最高的二进制方式进行序列化

  • 网络通信

    Dubbo采用Socket通信,自定义一套高效的通讯协议,提升了通信效率,并且可以建立长连接,不用反复连接,极大的提升了传输的效率

现在市面上还有很多的RPC框架,如:gRPC、Thrift、HSF等等

Dubbo除了提供远程服务调用的功能之外,还有服务注册发现的功能,我们来看一下官方的Dubbo架构图:

image-20220806232532540

可以看到在Dubbo中主要有五个角色:

  • Container: 服务运行容器,负责加载、运行服务提供者。必须。

  • Provider: 暴露服务的服务提供方,会向注册中心注册自己提供的服务。必须。

  • Consumer: 调用远程服务的服务消费方,会向注册中心订阅自己所需的服务。必须。

  • Registry: 服务注册与发现的注册中心。注册中心会返回服务提供者地址列表给消费者。非必须。

  • Monitor: 统计服务的调用次数和调用时间的监控中心。服务消费者和提供者会定时发送统计数据到监控中心。 非必须。

读者可能会感觉这些服务好像Nacos也提供叭,或者说是SpringCloud也提供,是的,其实DubboSpringCloud在某种程度上是竞争关系

那这里就有疑问了:dubbo和springCloud各有什么优缺点呢?这个问题放到文章最后面讨论,我们接着往下看

Dubbo为我们提供的主要功能有:

  1. 面向接口代理的高性能 RPC 调用
  2. 智能容错和负载均衡。
  3. 服务自动注册和发现
  4. 高度可扩展能力
  5. 运行期流量调度
  6. 可视化的服务治理与运维

我们总结一下Dubbo的作用: Dubbo是一站式的微服务解决方案

2. Dubbo快速上手

因为Dubbo是使用Zookeeper进行服务注册发现,所以我们需要安装一个zk,后续可以使用Nacos作为注册中心

我们可以在Linux环境下搭建一个Zookeeper,这里笔者采用的方式是Docker的方式

2.1、docker安装zookeeper

下载zookeeper 最新版镜像

docker search zookeeper    
docker pull zookeeper 
docker images              //查看下载的本地镜像
docker inspect zookeeper   //查看zookeeper详细信息

新建一个文件夹

mkdir zookeeper

挂载本地文件夹并启动服务

docker run -d -e TZ="Asia/Shanghai" -p 2181:2181 -v /root/docker/zookeeper:/data --name zookeeper --restart always zookeeper

参数解释

-e TZ="Asia/Shanghai" # 指定上海时区 
-d # 表示在一直在后台运行容器
-p 2181:2181 # 对端口进行映射,将本地2181端口映射到容器内部的2181端口
--name # 设置创建的容器名称
-v # 将本地目录(文件)挂载到容器指定目录;
--restart always #始终重新启动zookeeper

查看容器

docker ps

进入容器(zookeeper)

第一种方式

 docker run -it --rm --link zookeeper:zookeeper zookeeper zkCli.sh -server zookeeper       //这样的话,直接登录到容器时,进入到 zkCli中

第二种方式(推荐)

docker exec -it zookeeper bash      //只登录容器,不登录 zkCli
./bin/zkCli.sh    //执行脚本新建一个Client,即进入容器

2.2、各模块的配置文件

我们这里来新建一个项目用来演示一下Dubbo的使用过程:

在这里插入图片描述

父模块的依赖如下,采用的都是现在最新的版本,如果版本发生冲突可以试着降低版本

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.hnit</groupId>
    <artifactId>dubbo_demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>dubbo_demo</name>
    <description>dubbo_demo</description>

    <modules>
        <module>dubbo-api</module>
        <module>dubbo-service-provider</module>
        <module>dubbo-web-consumer</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
        <dubbo-boot.version>3.0.8</dubbo-boot.version>
        <zookeeper.version>4.0.0</zookeeper.version>
        <web.version>2.6.4</web.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- apache 官方 3.0 starter依赖 -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo-boot.version}</version>
            </dependency>
            <!-- zookeeper客户端  只需引入此依赖curator-framework curator-recipes 都有 -->
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-x-discovery</artifactId>
                <version>${zookeeper.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${web.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

dubbo-api依赖:

    <parent>
        <groupId>com.hnit</groupId>
        <artifactId>dubbo_demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <groupId>com.hnit</groupId>
    <artifactId>dubbo-api</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

dubbo-services-provider依赖:

    <parent>
        <groupId>com.hnit</groupId>
        <artifactId>dubbo_demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <groupId>com.hnit</groupId>
    <artifactId>dubbo-service-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-service-provider</name>
    <description>dubbo-service-provider</description>

    <dependencies>
        <!-- 不需要对外暴露接口,仅需要给其他模块进行RPC调用 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--ZooKeeper-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
        </dependency>
        <!--dubbo-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

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

dubbo-services-provider配置文件:

# 这里的配置属性只是基础配置,如需更多功能配置,请自行扩展
spring:
  application:
    name: dubbo-service-provider
server:
  port: 9101
dubbo:
  application:
    name: dubbo-service
  #配置dubbo包扫描
  scan:
    base-packages: com.hnit.provider.service.impl
  #配置注册中心的地址
  registry:
    address: zookeeper://localhost:2181
  #元数据配置
  metadata-report:
    address: zookeeper://localhost:2181
  #Dubbo协议配置
  protocol:
    name: dubbo
    port: 30882
  #超时重试时间
  provider:
    timeout: 3000
  qos:
    enable: true # 启用QoS功能
    port: 55555 # 指定QoS服务端口号

若想配置第二个dubbo-services-provider,只是要将dubbo通信的端口修改一下就好

dubbo-web-comsumer依赖:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-x-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.hnit</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

配置文件:

spring:
  application:
    name: dubbo-web-consumer
server:
  port: 9102
dubbo:
  application:
    name: dubbo-web
  registry:
    address: zookeeper://localhost:2181
  consumer:
    timeout: 3000
  protocol:
    name: dubbo
    port: 30882

接着我们在API中写几个需要进行RPC的接口,这个包一般用来定义接口,提供给其他包进行实现,读者也可以不写这个包

在这里插入图片描述

User类

/**
 *  所有的pojo类都需要实现Serializable接口
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 8728327146677888239L;
    
    private Integer id;
    private String username;
    private String password;
}

接口UserService

public interface UserService {
    User getByUserId(Integer userId);
}

2.3、对外暴露接口

接着我们写调用逻辑和对外暴露的接口,对外暴露的接口仅写在dubbo-consumer模块中,在此之前我们需要先写服务中心的RPC调用逻辑,也就是dubbo-services-provider模块中,写一个类来实现之前我们dubbo-api中定义的接口

@Slf4j
@DubboService
public class UserServiceImpl implements UserService {
    // 模拟数据
    private static final List<User> USERS = Arrays.asList(
            new User(1, "张三"),
            new User(2, "李四")
    );
    // 用来记录被调用的次数
    private final AtomicInteger sum = new AtomicInteger(0);

    @Override
    public User getByUserId(Integer userId) {
        // 打印一下被调用情况,dubbo-provider2中这里填dubbo-provider2被调用
        log.info("dubbo-provider被调用【{}】次", sum.incrementAndGet());
        for (User user : USERS) {
            if (user.getUserId().equals(userId)) {
                return user;
            }
        }
        return null;
    }
}

启动类上我们需要加上@EnableDubbo注解表示启动Dubbo RPC

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

dubbo-web-consumer模块

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

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

    @RequestMapping("/getByUserId")
    public User getByUserId(Integer userId) {
        return userService.getByUserId(userId);
    }

}

启动类同样加@EnableDubbo注解

2.4、查看调用情况

接下来我们启动dubbo-web-consumerdubbo-services-provider,注意我们这里要先启动 RPC提供的服务,再启动第一个对外暴露接口的服务

我们来测试一下,发现可以成功完成调用:

在这里插入图片描述

3、dubbo高级特性

3.1、dubbo-admin

  • dubbo-admin 管理平台,是图形化的服务管理页面

  • 从注册中心中获取到所有的提供者 / 消费者进行配置管理

  • 路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能

  • dubbo-admin 是一个前后端分离的项目。前端使用vue,后端使用springboot

  • 安装 dubbo-admin 其实就是部署该项目

在这里插入图片描述

在这里插入图片描述

3.2、负载均衡

负载均衡策略(4种):

  • Random :按权重随机,默认值。按权重设置随机概率。

  • RoundRobin :按权重轮询。

  • LeastActive:最少活跃调用数,相同活跃数的随机。

  • ConsistentHash:一致性 Hash,相同参数的请求总是发到同一提供者。

这里我们再加一个服务提供者dubbo-services-provider2

我们来看看ZooKeeper上的结点情况:

在这里插入图片描述

3.3、序列化

在这里插入图片描述

3.4、地址缓存

注册中心挂了,服务是否可以正常访问?

  • 可以,因为dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。

  • 当服务提供者地址发生变化时,注册中心会通知服务消费者。

3.5、超时与重试

在这里插入图片描述

  • 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。

  • 在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。

在这里插入图片描述

  • 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。

  • 在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。

  • dubbo 利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。

  • 使用timeout属性配置超时时间,默认值1000,单位毫秒。

在这里插入图片描述

  • 设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。

  • 如果出现网络抖动,则这一次请求就会失败。

  • Dubbo 提供重试机制来避免类似问题的发生。

  • 通过 retries 属性来设置重试次数。默认为 2 次。

3.6、多版本

  • 灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。

  • dubbo 中使用version 属性来设置和调用同一个接口的不同版本

    在这里插入图片描述

3.7、集群容错

集群容错模式:

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

  • Failfast Cluster :快速失败,只发起一次调用,失败立即报错。通常用于写操作。

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

  • Failback Cluster :失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

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

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

4、使用nacos作为注册中心

在上面的快速入门中,我们知道了Dubbo进行RPC调用的过程,我们可以想注入一个普通Bean一样,注入一个远程的Bean,并且还可以配置负载均衡策略,到这里Dubbo中的五个核心组件我们已经见识过四个了,其中Container容器就是我们的Spring容器,现在只有最后一个组件:Monitor没有见过了,接下来我们就开始学习它

Monitor是监视器的意思,用来监视整个Dubbo组件的状态,它提供了一个web界面让我们更清楚在查看我们的Dubbo使用情况,由于Monitor本身存在一些问题,一般我们会使用dubbo-admin进行管理,但是 dubbo-2.6.1以后的版本不再有dubbo-admin

现在一般我们会使用Nacos作为注册中心和配置管理中心,接下来,我们使用Nacos重复一遍上面的过程

image-20220808003201046

这里我在docker上面安装一下,这里需要注意的是Nacos进行RPC通信的端口是98489849,所以这两个端口也需要开放

image-20220821151534355

docker run -d \
-e MODE=standalone \
--privileged=true \
--restart=always \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
--log-opt max-size=500m \
-p 8848:8848  \
-p 9848:9848  \
-p 9849:9849  \
--name nacos \
--restart=always \
nacos/nacos-server:1.4.1        

在浏览器中输入:http://你的ip地址:8848/nacos,即可访问(账号密码都是Nacos),如果是云服务器记得开安全组

4.1、nacos 添加命名空间

我们需要在Nacos上添加一个命名空间,用来专门作为Dubbo的空间

在这里插入图片描述

在这里插入图片描述

4.2、添加依赖

这里的方式是非SpringCloud的方式,后面再看SpringCloud的方式

首先我们需要改造一下之前的pom依赖,将SpringCloudNacos的依赖添加进去

首先在父工程的pom文件中的<dependencyManagement>中引入Dubbo Registry Nacos的依赖:

然后在对应的consumerprovider中也要引入这个依赖,版本和自己Nacos依赖一样即可

<!-- Dubbo Registry Nacos -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-nacos</artifactId>
    <version>3.0.8</version>
</dependency>

consumerprovider的application.yml中添加nacos地址:

# 这里的配置属性只是基础配置,如需更多功能配置,请自行扩展
dubbo:
  application:
    name: dubbo-provider # 这里取名字的时候要区分开
  registry:
    address: nacos://localhost:8848
    parameters:
      namespace: 60f00a4f-2f26-4b5d-8f88-7fe63b85455f
  protocol:
    name: dubbo
    port: 20880

4.3、服务调用

在这里插入图片描述

5、demo源码

King Giji/dubbo_demo - 码云 - 开源中国 (gitee.com)

Dubbo是一个分布式服务框架,可以实现服务的注册与发现、远程通信、负载均衡等功能。为了学习和使用Dubbo,我们可以搭建一个Dubbo demo来进行实践。 首先,我们需要准备好开发环境。我们可以使用Maven构建项目,代码使用Java编写。可以选择任意一个Java开发工具,比如Eclipse或者IntelliJ IDEA。 接下来,我们需要下载Dubbo的安装包。可以从Dubbo的官方网站上进行下载,选择适合自己的版本。然后解压到本地目录。 然后,我们创建一个Maven项目,并在pom.xml文件中添加Dubbo的依赖。可以在Dubbo的安装包中找到pom.xml的路径,将其复制到我们的项目中。 在项目中创建一个服务提供者和一个服务消费者的模块。服务提供者是一个提供服务的服务端,服务消费者是一个调用服务的客户端。 在服务提供者模块中,我们需要定义一个接口,并实现该接口。接口定义了服务的功能,实现类为具体的业务逻辑。 在服务消费者模块中,我们需要配置Dubbo的相关信息,比如注册中心的地址以及要消费的服务提供者的接口。然后我们可以通过调用该接口的方法来使用远程服务。 最后,我们分别启动服务提供者和服务消费者模块。可以通过命令行或者在开发工具中直接运行。 通过以上的步骤,我们就成功搭建了一个Dubbo demo。我们可以在服务提供者和服务消费者之间进行远程调用,并观察调用结果。这个demo可以帮助我们理解和学习Dubbo的基本使用方法和原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

King Gigi.

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值