万字长文Dubbo 入门总结 ,一款高性能的 Java RPC 框架

Posted 理想二旬不止

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了万字长文Dubbo 入门总结 ,一款高性能的 Java RPC 框架相关的知识,希望对你有一定的参考价值。

这篇文章是我学习整理 Dubbo 的一篇文章,首先大部分内容参考了官网 + 某硅谷的视频,内容讲解进行了重新编排,40多张图片,也都是我修改重制的,虽然一万多字,但是其实也可以看出来,更多的内容集中在了概念或功能的介绍,具体环境的搭建,以及如何配置,快速上手上面,但是对于这样一款优秀的框架,文章中提到的每一个点其实展开来讲都能写这样篇幅的一篇文章,仅仅入门来看也没必要,总得学会走,才可以去试着跑

  • 文章提及了 SSM 和 Springboot 下的不同配置方式,但是 Zookeeper 环境搭在了 Windows 下,后面准备重新整理的 Redis 我考虑在 Linux 中来跑,会更贴近实际一些

  • 说句题外话,设计模式也没停,还有一篇存稿,不过想整理的东西太多,还有学校等等的事情,真有点自闭了

  • 有什么建议和意见欢迎在评论区和我交流哈,感谢各位大佬的支持~

早安,学习人 & 打工人 ~ ~ ~

一 必备基础知识

(一) 分布式基础理论

在百度以及维基中的定义都相对专业且晦涩,大部分博客或者教程经常会使用《分布式系统原理和范型》中的定义,即:“分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像是单个相关系统”

下面我们用一些篇幅来通俗的解释一下什么叫做分布式

(1) 什么是集中式系统

提到分布式,不得不提的就是 “集中式系统”,这个概念最好理解了,它就是将功能,程序等安装在同一台设备上,就由这一台主机设备向外提供服务

举个最简单的例子:你拿一台PC主机,将其改装成了一台简单的服务器,配置好各种内容后,你将mysql,Web服务器,FTP,nginx 等等,全部安装在其中,打包部署项目后,就可以对外提供服务了,但是一旦这台机器无论是软件还是硬件出现了问题,整个系统都会受到严重的牵连错误,鸡蛋放在一个篮子里,要打就全打了

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

(2) 什么是分布式系统

既然集中式系统有这样一种牵一发而动全身的问题,那么分布式的其中一个作用,自然是来解决这样的问题了,正如定义中所知,分布式系统在用户的体验感官里,就像传统的单系统一样,一些变化都是这个系统本身内部进行的,对于用户并没有什么太大的感觉

例如:淘宝,京东这种大型电商平台,它们的主机都是数以万计的,否则根本没法处理大量的数据和请求,具体其中有什么划分,以及操作,我们下面会说到,但是对于用户的我们,我们不需要也不想关心这些,我们仍可以单纯的认为,我们面对的就是 “淘宝” 这一台 “主机”

所以分布式的一个相对专业一些的说法是这样的(进程粒度)两个或者多个程序,分别运行在不同的主机进程上,它们互相配合协调,完成共同的功能,那么这几个程序之间构成的系统就可以叫做分布式系统

  • 这几者都是相同的程序 —— 分布式

  • 这几者都是不同的程序 —— 集群

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

(3) 发展过程

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

A:单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

优点:

  • 所有功能集中在一起,开发以及后期维护均相对简单

  • 对于小型网站及管理系统,简单易用

缺点:

  • 对于大型项目,升级维护困难

  • 无法对于一个模块进行水平扩展或优化

  • 模块之间耦合紧密,容错率低

B:垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

优点:

  • 通过切分业务达到各模块独立部署,维护和部署更加简单

  • 可以对一个模块进行水平扩展或优化

缺点:

  • 系统之间无法相互调用

  • 具有重复代码,且公用模块无法重复利用

C:分布式服务架构

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

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

优点:

  • 抽取公共代码,代码复用性提高

缺点:

  • 调用关系复杂,维护很麻烦

D:流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

优点:

  • 复杂的调用关系不再需要自行维护

缺点:

  • 服务具有依赖性,一个服务出现问题或许会导致其他系统不可用

(4) RPC 介绍

这应该是很好理解的,调用本地 A 服务器上的函数或者方法的时候很简单,但是如果A 想要访问 B 的方法时,两者的内存空间压根都不是同一个,只能通过网络传输调用的相关内容,关于传输协议亦或者传输方式,都由 RPC 帮我们背后实现

也就是说,即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同

关于 RPC 更加细致专业的解释说明

RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易

RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行

某乎:谁能用通俗的语言解释一下什么是 RPC 框架?

本地过程调用

RPC就是要像调用本地的函数一样去调远程函数。在研究RPC前,我们先看看本地调用是怎么调的。假设我们要调用函数Multiply来计算lvalue * rvalue的结果:

……省略,此篇可细细看一下

问题答者:洪春涛

来看一下从 A 想要访问 B 中方法的一个流程例子

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

顺着执行路线来捋一下

  • ① 客户端 Client 发出一个调用远程服务的信号

  • ② Client Stub 接收到调用,把方法参数进行序列化处理

  • ③ 客户端 Client 通过 Sockets 将消息发送到服务端

  • ④ 服务端的 Server Stub,对收到的消息进行解码,也就是反序列化的过程

  • ⑤ 服务端 Server Stub,根据解码结果,调用服务端的本地服务

  • ⑥ 服务端的本地服务执行,且将结果返回给自己的 Server Stub

  • ⑦ Server Stub 打包结果成消息,即序列化结果消息

  • ⑧ 服务端通过 Sockets 将消息发送到客户端

  • ⑨ 客户端的 Client Stub 接收到服务端传来的结果消息,进行解码处理,即反序列化

  • ⑩ 客户端得到从服务端传来的最后结果

第③点说明:RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

(二) Dubbo 概念

(1) 介绍

Dubbo官网:http://dubbo.apache.org/zh/

Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架

Apache Dubbo |ˈdʌbəʊ| 提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。

面向接口代理的高性能RPC调用

  • 提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。

智能负载均衡

  • 内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。

服务自动注册与发现

  • 支持多种注册中心服务,服务实例上下线实时感知。

高度可扩展能力

  • 遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。

运行期流量调度

  • 内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。

可视化的服务治理与运维

  • 提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。

(2) 架构

这是Dubbo的架构图

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

首先介绍一下这五个节点的角色(五个圆角矩形框)

  • Provider:暴露服务的服务提供方

  • Consume:调用远程服务的服务消费方

  • Registry:服务注册与发现的注册中心

  • Monitor:统计服务的调用次数和调用时间的监控中心

  • Container:服务运行容器

再来看一下调用的关系和流程:

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

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

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

(三) dubbo 环境搭建

说明:以下为学习演示方便,均为 Windows 系统搭建,真实 Linux 场景搭建会在后面出一篇,如何在Linux安装配置常见的软件

(1) 搭建zookeeper注册中心环境

A:下载 zookeeper

首先先将 zookeeper  下载下来

官网(查看文档,下载):https://zookeeper.apache.org/

嫌麻烦就直接去下载页面:https://downloads.apache.org/zookeeper/

这里下载的版本是:apache-zookeeper-3.6.2

注意:下载,apache-zookeeper-3.6.2.tar.gz ,apache-zookeeper-3.6.2-bin.tar.gz,虽然本质我们是用前者,但是启动过程中遇到了报错,需要后者部分文件来进行修复

B:配置及排错

XXXapache-zookeeper-3.6.2in,下面的 zkServer.cmdzkCli.cmd 就是我们想要运行的,可以在当前目录下打开 cmd(什么都行) 运行 zkServer.cmd ,首先可能会遇到第一个错误——找不到或无法加载主类

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

这个时候就需要解压刚才下载的 apache-zookeeper-3.6.2-bin.tar.gz,然后在其目录下,复制整个lib文件夹到刚才的 apache-zookeeper-3.6.2 文件夹下,这样就解决了

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

再次运行,会有第二个问题:一个配置文件的缺失

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

这个问题只需要在 XXXapache-zookeeper-3.6.2conf  中把 zoo_sample.cfg文件复制一份,且名字改为zoo.cfg

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

C:运行服务端及客户端

都解决好了,来运行 zkServer.cmd,就启动成功了

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

运行zkCli.cmd,可以成功连接到 zookeeper的服务器

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

服务端客户端都跑起来了,搭建zookeeper注册中心环境到这里就算 OK了

(2) 配置 zookeeper监控中心

dubbo本身并不是一个服务软件。它其实就是一个jar包能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。所以你不用在Linux上启动什么dubbo服务

但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序——dubbo-admin,不过这个监控即使不装也不影响使用

A:下载 dubbo-admin

从 github 上 down 到本地 ,有 dubbo-admin-master 一个文件夹,其中含有三个主要文件夹

  • dubbo-admin

  • dubbo-monitor-simple

  • dubbo-registry-simple

B:先确认配置正确

我们现在先只针对 dubbo-admin 操作

打开编辑配置文件:XXXdubbo-admin-masterdubbo-adminsrcmain esourcesapplication.properties

dubbo.registry.address=zookeeper://127.0.0.1:2181

C:打包运行

既然没问题了,就在 xxdubbo-admin-masterdubbo-admin,这个文件夹下进行打包,用 cmd 也都一样,命令如下:

mvn clean package
【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

打包成功后,target 文件夹中会多出一个 dubbo-admin-0.0.1-SNAPSHOT.jar ,我们把它复制到一个自己指定的位置,然后通过 cmd 运行这个 jar

java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

其实就是运行了一个 springboot 的项目,然后控制台会有写到是一个 7001 端口,来去访问一下,用户名,密码都是 root,出现如下所图就是成功了

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

注意:要想访问到这个页面,请保证 zookeeper 注册中心启动了

(四) 第一个 Dubbo 项目

我们第一个例子就照着这个网络上参考的例子来做(代码并不重要,关键是理解调用关系和配置方式):

顺便提一句,很清楚的可以看出来,用户服务是被调用的,所有它就是服务的提供者,而订单服务作为消费者

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

(1) 创建项目及各个模块

为了在一台机器上演示,我首先创建了一个 Maven 的空项目,然后分别创建几个子模块(程序很简单,不需要Maven骨架)

A:创建用户服务(服务提供者)

在空项目中创建第一个模块 user-service-provider

首先创建出实体和业务层接口以及实现类

实体:UserAddress

public class UserAddress implements Serializable {
    private Integer id;
    private String userAddress; //用户地址
    private String userId; //用户id
    private String consignee; //收货人
    private String phoneNum; //电话号码
    private String isDefault; //是否为默认地址    Y-是     N-否

    // 请自行补充 get set 构造方法 toString

}

业务层接口:UserService

public interface UserService {
    /**
     * 按照用户id返回所有的收货地址
     * @param userId
     * @return
     */

    public List<UserAddress> getUserAddressList(String userId);
}

业务层实现类:UserServiceImpl

public class UserServiceImpl implements UserService {
    public List<UserAddress> getUserAddressList(String userId) {

        UserAddress address1 = new UserAddress(1"广东省xxx市xxx区xxx路xxx小区24栋1单元801户""1""阿文""13999999999""Y");
        UserAddress address2 = new UserAddress(2"北京市yyy区yyy路yyy小区19号3单元502户""1""北方少女的梦""13888888888""N");

        return Arrays.asList(address1,address2);
    }
}

程序很简单,数据都是拟出来的

以下是这个模块的结构图:

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

B:创建订单服务(服务消费者)

在项目中创建第二个模块 order-service-consumer

业务层接口:OrderService

public interface OrderService {
    /**
     * 初始化订单
     * @param userID
     */

    public void initOrder(String userID);
}

业务层实现类:OrderServiceImpl

现在还是一个空实现,后面会补充好

public class OrderServiceImpl implements OrderService {
    public void initOrder(String userID) {
        //查询用户的收货地址
    }
}
【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

有一种方式,就是将服务提供者,也就是用户服务的实体类以及它的 UserService  接口,复制到我们这个订单的模块中,但是总不能以后有一个地方调用就复制一次吧,这也太麻烦了

所以通用的做法是再创建一个模块,将服务接口,模型等等全放在一起,维护以及调用会更好

C:创建接口模块

在项目中创建:mall-interface 模块

将用户模块(服务提供者)和订单模块(服务消费者) 中的所有实体类和service接口复制到当前模块下

删除原有的实体类包及service包,也就是将实体类及service放在了当前公共的项目中了

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

既然原来两个模块都删掉了实体等内容,我们想要用到这个公共的接口模块,只需要引入依赖即可

<dependency>
    <groupId>cn.ideal.mall</groupId>
    <artifactId>mall-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

(2) 服务提供者配置及测试

A:引入依赖

我们先对用户服务进行配置,首先要引入dubbo和zookeeper客户端的依赖

至于版本,可以去maven中去查

由于我们使用 zookeeper 作为注册中心,所以需要操作 zookeeper
dubbo 2.6 以前的版本引入 zkclient 操作 zookeeper
dubbo 2.6 及以后的版本引入 curator 操作 zookeeper
下面两个 zk 客户端根据 dubbo 版本 2 选 1 即可

Dubbo 用了 2.6.9

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

curator 用了 5.1.0

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

B:创建配置文件

在 resource 文件中创建 provider.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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
      http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"
>

    <!--1、指定当前服务/应用的名字(同样的服务名字相同,不要和别的服务同名)-->
    <dubbo:application name="user-service-provider"></dubbo:application>
    <!--2、指定注册中心的位置-->
    <!--<dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>-->
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>
    <!--3、指定通信规则(通信协议? 服务端口)-->
    <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol>
    <!--4、暴露服务 让别人调用 ref 指向服务的真正实现对象,通过ref引用下面自定义的 bean-->
    <dubbo:service interface="cn.ideal.mall.service.UserService" ref="userServiceImpl"></dubbo:service>

    <!--服务的实现-->
    <bean id="userServiceImpl" class="cn.ideal.mall.service.impl.UserServiceImpl"></bean>

</beans>

C:创建一个启动类

public class MailApplication {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:provider.xml");
        applicationContext.start();
        System.in.read();
    }
}

如果除了 slf4j 以外没有报出什么警告或者异常,就是成功了

  • 别忘了打开,zookeeper注册中心的 zkServer.cmd、和zkCli.cmd服务

  • 还有运行 java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

然后继续访问 dubbo-admin 的管理页面 http://localhost:7001/,在服务治理的提供者中,已经可以看到发现了这个提供者

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

(3) 服务消费者配置及测试

A:引入依赖

<!--dubbo-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.6.9</version>
</dependency>
<!--注册中心是 zookeeper,引入zookeeper客户端-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.1.0</version>
</dependency>

B:创建配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
      http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
      http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"
>

    <!--包扫描-->
    <context:component-scan base-package="cn.ideal.mall.service.impl"/>

    <!--指定当前服务/应用的名字(同样的服务名字相同,不要和别的服务同名)-->
    <dubbo:application name="order-service-consumer"></dubbo:application>
    <!--指定注册中心的位置-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>

    <!--调用远程暴露的服务,生成远程服务代理-->
    <dubbo:reference interface="cn.ideal.mall.service.UserService" id="userService"></dubbo:reference>

    <!--dubbo-monitor-simple监控中心发现的配置-->
    <dubbo:monitor protocol="registry"></dubbo:monitor>
    <!--<dubbo:monitor address="127.0.0.1:7070"></dubbo:monitor>-->

</beans>

C:补充订单业务实现类代码

这个实现类,刚才还空着,经过引入接口依赖,Dubbo 等依赖以及配置,已经可以调用了,调用一下这个方法,当然下面的输出语句完全可以不写,就是为了一个观察方便

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    public UserService userService;
    public void initOrder(String userID) {
        //查询用户的收货地址
        List<UserAddress> userAddressList = userService.getUserAddressList(userID);

        //为了直观的看到得到的数据,以下内容也可不写
        System.out.println("当前接收到的userId=> "+userID);
        System.out.println("**********");
        System.out.println("查询到的所有地址为:");
        for (UserAddress userAddress : userAddressList) {
            //打印远程服务地址的信息
            System.out.println(userAddress.getUserAddress());
        }
    }
}

C:创建启动类

public class ConsumerApplication {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
        OrderService orderService = applicationContext.getBean(OrderService.class);

        //调用方法查询出数据
        orderService.initOrder("1");
        System.out.println("调用完成...");
        System.in.read();
    }
}

运行后,继续去 查看 http://localhost:7001/

可以看到,消费者也被发现了,同时控制台也成功的输出了内容

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

到这里,其实一个简单的调用过程已经完成了

(4) 安装简易的监控中心

dubbo-monitor-simple——简易监控中心

其实这个东西就是刚才 dubbo-admin-master 这个文件夹下除了dubbo-admin 的其中一个,本质也是一个图形化的界面,方便查看服务提供和消费者的信息

首先还是打包,然后 target 文件夹中会生成这个文件     dubbo-monitor-simple-2.0.0.jar 以及 dubbo-monitor-simple-2.0.0-assembly.tar.gz

将 dubbo-monitor-simple-2.0.0-assembly.tar.gz 解压出来,解压后config文件查看properties的配置是否是本地的zookeeper,配置文件的位置如下:

D:developdubbo-monitor-simple-2.0.0confdubbo.properties

  • D:develop 是我的路径

因为前面可能运行的问题,我后面有一些端口占用的问题,所以我把 dubbo.jetty.port=8080 修改成了 8081,这些可以根据需要自行修改

进入assembly.bin 文件夹,然后双击运行 start.bat 出现以下内容即启动成功

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

在服务者和消费者的 XML 文件中添加以下内容

<!--dubbo-monitor-simple监控中心发现的配置-->
<dubbo:monitor protocol="registry"></dubbo:monitor>
<!--<dubbo:monitor address="127.0.0.1:7070"></dubbo:monitor>-->

然后启动这两个模块的启动类

注:这时候要保证 zookeeper 服务客户端等前面的内容保持开着

访问localhost:8081,可以看到一个监控中心

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

点进去 Services 可以看到服务提供者和消费者的信息

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

到这里,这个监控中心也算安装好了!!!

(五) SpringBoot 整合 Dubbo

上面这个项目就是一个普通的 maven 项目,通过 XML 配置文件进行配置,在 SSM 项目中就可以这样使用,而 SpringBoot 作为现在开发的主力军之一,自然也要再讲一下它的一个配置运行方式

(1) 创建用户服务(服务提供者)

创建一个 SpringBoot 项目,名为:boot-user-service-provider

导入依赖

根据其 github 中的提示,因为我们选择的是 2.6.9 所以我们选择 0.2.1.RELEASE 这个版本就行了

https://github.com/apache/dubbo-spring-boot-project/blob/master/README_CN.md

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

https://github.com/apache/dubbo-spring-boot-project/blob/0.2.x/README_CN.md

点进去查看 0.2.1.RELEASE,根据其提示导入依赖

注:0.2.0 的版本中,导入 dubbo-spring-boot-starter 即同步背后帮你导入了 dubbo,curator等等,但是我拿 0.2.1 的版本测试的时候却发现并没有(可能是我的问题),所以你也不能运行,可以考虑像我一样,显式的引入这些内容

记得别忘了引入这个我们自定义公共的接口模块喔

<dependency>
    <groupId>cn.ideal.mall</groupId>
    <artifactId>mall-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<!-- Dubbo Spring Boot Starter -->
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>0.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.6.9</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
</dependency>

<!--注册中心是 zookeeper,引入zookeeper客户端-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.1.0</version>
</dependency>

在我们刚才的 user-service-provider 中将 service 的实现类按路径复制过来

注意:这个 @Service 是 dubbo 的,而 @Component 是因为,如果仍使用 Spring 的 @Service 会使得 dubbo 的那个以全称显示,不是很好看,不过你非要的话,也可以哈

package cn.ideal.mall.service.impl;

import cn.ideal.mall.pojo.UserAddress;
import cn.ideal.mall.service.UserService;
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @ClassName: UserServiceImpl
 * @Author: BWH_Steven
 * @Date: 2020/12/2 20:58
 * @Version: 1.0
 */

@Service
@Component
public class UserServiceImpl implements UserService {
    public List<UserAddress> getUserAddressList(String userId) {

        UserAddress address1 = new UserAddress(1"广东省xxx市xxx区xxx路xxx小区24栋1单元801户""1""阿文""13999999999""Y");
        UserAddress address2 = new UserAddress(2"北京市yyy区yyy路yyy小区19号3单元502户""1""北方少女的梦""13888888888""N");

        return Arrays.asList(address1,address2);
    }
}

配置 application.properties

dubbo.application.name=boot-user-service-provider
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper

dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

#连接监控中心
dubbo.monitor.protocol=registry

spring.main.allow-bean-definition-overriding=true

server.port=8082

添加启动类注解

@EnableDubbo // 开启基于注解的dubbo功能
@SpringBootApplication
public class BootUserServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootUserServiceProviderApplication.class, args);
    }
}

(2) 创建订单服务(服务消费者)

创建 springboot 项目boot-order-service-consumer ,此项目应该是一个 web 项目,注意引入 web 的 starter

同样引入一样的依赖

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

<dependency>
    <groupId>cn.ideal.mall</groupId>
    <artifactId>mall-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<!-- Dubbo Spring Boot Starter -->
<dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>0.2.1.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.6.9</version>
</dependency>

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
</dependency>

<!--注册中心是 zookeeper,引入zookeeper客户端-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.1.0</version>
</dependency>

把之前 order-service-consumer 项目中的 service 实现类按路径复制过来

有两点注意:

  • @Autowired ==> @Reference

  • 因为是一个 web 项目,要在浏览器查看结果,所以将结果从 void 改成 List 返回回去,因此需要修改公共的接口模块中的此方法接口,将返回类型改为 List

package cn.ideal.mall.service.impl;

import cn.ideal.mall.pojo.UserAddress;
import cn.ideal.mall.service.OrderService;
import cn.ideal.mall.service.UserService;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @ClassName: OrderServiceImpl
 * @Author: BWH_Steven
 * @Date: 2020/12/2 22:38
 * @Version: 1.0
 */

@Service
public class OrderServiceImpl implements OrderService {

    @Reference
    public UserService userService;

    public List<UserAddress> initOrder(String userID) {
        //查询用户的收货地址
        List<UserAddress> userAddressList = userService.getUserAddressList(userID);

        //为了直观的看到得到的数据,以下内容也可不写
        System.out.println("当前接收到的userId=> "+userID);
        System.out.println("**********");
        System.out.println("查询到的所有地址为:");
        for (UserAddress userAddress : userAddressList) {
            //打印远程服务地址的信息
            System.out.println(userAddress.getUserAddress());
        }
        return userAddressList;
    }
}

编写 controller

@Controller
public class OrderController {
    @Autowired
    OrderService orderService;

    @RequestMapping("/initOrder")
    @ResponseBody
    public List<UserAddress> initOrder(@RequestParam("uid")String userId) {
        return orderService.initOrder(userId);
    }
}

配置 application.properties

dubbo.application.name=boot-order-service-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181

#连接监控中心 注册中心协议
dubbo.monitor.protocol=registry

spring.main.allow-bean-definition-overriding=true

server.port=8083

启动类添加注解

@EnableDubbo // 开启基于注解的dubbo功能
@SpringBootApplication
public class BootOrderServiceConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootOrderServiceConsumerApplication.class, args);
    }
}

(3) 测试

首先保证打开了,zookeeper注册中心的 zkServer.cmd、和zkCli.cmd服务

还有运行 java -jar dubbo-admin-0.0.1-SNAPSHOT.jar ,想开的话还可以打开监控中心 dubbo-monitor-simple-2.0.0

然后就可以运行服务提供者 boot-user-service-provider ,然后运行 服务消费者 boot-order-service-consumer ,运行成功后可以看一下效果

这是使用 http://localhost:7001/  访问的结果

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

或者 http://localhost:8001  (我的监控中心端口设置的是 8081)

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

以及根据自己设置的项目端口号去请求

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

可以看到结果都是没问题的,SpringBoot 整合 Dubbo 就是这样一个方式

(4) 其他几种方式

一 将服务提供者注册到注册中心(如何暴露服务)

  • ① 导入Dubbo的依赖 和 zookeeper 客户端

Springboot与Dubbo整合的三种方式

  • ① 导入dubbo-starter。在application.properties配置属性,使用@Service【暴露服务】,使用@Reference【引用服务】

  • ② 保留Dubbo 相关的xml配置文件

  • 导入dubbo-starter,使用@ImportResource导入Dubbo的xml配置文件

  • 例如:在启动类添加 @ImportResource(locations = "classpath:provider.xml")

  • ③ 使用 注解API 的方式

  • 将每一个组件手动配置到容器中,让dubbo来扫描其他的组件

例如创建一个 config,其本质就是为了放弃掉 xml 和配置文件,这种方式在学习 Spring 配置的时候也有用过哈

@Configuration
public class MyDubboConfig {

    @Bean
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("boot-user-service-provider");
        return applicationConfig;
    }

    //<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry>
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress("127.0.0.1:2181");
        return registryConfig;
    }

    //<dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
    @Bean
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(20882);
        return protocolConfig;
    }

    /**
     *<dubbo:service interface="com.atguigu.gmall.service.UserService" 
        ref="userServiceImpl01" timeout="1000" version="1.0.0">
        <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
    </dubbo:service>
     */

    @Bean
    public ServiceConfig<UserService> userServiceConfig(UserService userService){
        ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
        serviceConfig.setInterface(UserService.class);
        serviceConfig.setRef(userService);
        serviceConfig.setVersion("1.0.0");

        //配置每一个method的信息
        MethodConfig methodConfig = new MethodConfig();
        methodConfig.setName("getUserAddressList");
        methodConfig.setTimeout(1000);

        //将method的设置关联到service配置中
        List<MethodConfig> methods = new ArrayList<>();
        methods.add(methodConfig);
        serviceConfig.setMethods(methods);

        //ProviderConfig
        //MonitorConfig

        return serviceConfig;
    }

}

二 Dubbo 配置

(一) 重写与配置优先级

http://dubbo.apache.org/zh/docs/v2.7/user/configuration/properties/

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

优先级从高到低:

  • JVM -D 参数:当你部署或者启动应用时,它可以轻易地重写配置,比如,改变 dubbo 协议端口;

  • XML:XML 中的当前配置会重写 dubbo.properties 中的;

  • Properties:默认配置,仅仅作用于以上两者没有配置时。

  1. 如果在 classpath 下有超过一个 dubbo.properties 文件,比如,两个 jar 包都各自包含了 dubbo.properties,dubbo 将随机选择一个加载,并且打印错误日志。

  2. 如果 id 没有在 protocol 中配置,将使用 name 作为默认属性。

(二) 启动时检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时候就会抛出异常,同时阻止 Spring 初始化完成,好处就是上线的时候可以及早的发现问题,注:默认 check=“true”

  • 可以通过 check=“false” 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。

  • 另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check=“false”,总是会返回引用,当服务恢复时,能自动连上

比如在 order-service-consumer 消费者中,在 consumer.xml 中添加配置

<!--配置当前消费者的统一规则,当前所有的服务都不启动时检查-->
<dubbo:consumer check="false"></dubbo:consumer>

也可以在每一个上面加 check

(三) 全局超时配置

由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时),为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间

(1) Dubbo消费端

<!--全局超时配置-->
<dubbo:consumer timeout="5000" />

<!--调用远程暴露的服务,生成远程服务代理-->
<dubbo:reference interface="cn.ideal.mall.service.UserService" id="userService" timeout="2000">
    <dubbo:method name="getUserAddressList" timeout="3000"/>
</dubbo:reference>

(2) Dubbo服务端

<!--全局超时配置-->
<dubbo:consumer timeout="5000" />

<!--调用远程暴露的服务,生成远程服务代理-->
<dubbo:service interface="cn.ideal.mall.service.UserService" ref="userServiceImpl" timeout="2000">
    <dubbo:method name="getUserAddressList" timeout="3000"/>
</dubbo:service>

设置超时时间其实算蛮简单的,但是最主要注意的问题就是优先级问题,在上面不管是消费者还是服务者,我都配置了三种层次的超时配置,这几者的优先级别简单总结就是:

  • ① 更细,更精准的优先:1. 方法级别 <== 2. 接口级别 <== 3. 全局级别

  • ② 消费者设置优先:级别一致的情况下,消费者优先于提供者

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

补充:

  • dubbo:consumer 超时默认值为 1000

http://dubbo.apache.org/zh/docs/v2.7/user/references/xml/dubbo-consumer/

属性 对应URL参数 类型 是否必填 缺省值 作用 描述
timeout default.timeout int 可选 1000 性能调优 远程服务调用超时时间(毫秒)

(3) 配置原则

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

  • 作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等

  • 在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作为Consumer的缺省值。否则,Consumer会使用Consumer端的全局设置,这对于Provider不可控的,并且往往是不合理的

(四) 多版本问题

https://dubbo.apache.org/zh/docs/v2.7/user/examples/multi-versions/#m-zhdocsv27userexamplesmulti-versions

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

可以按照以下的步骤进行版本迁移:

  • 在低压力时间段,先升级一半提供者为新版本

  • 再将所有消费者升级为新版本

  • 然后将剩下的一半提供者升级为新版本

老版本服务提供者配置:

<dubbo:service interface="cn.ideal.mall.service.UserService" ref="userServiceImpl" version="1.0.0"/>

<!--服务的实现-->
<bean id="userServiceImpl" class="cn.ideal.mall.service.impl.UserServiceImpl"/>

新版本服务提供者配置:

<dubbo:service interface="cn.ideal.mall.service.UserService" ref="userServiceImpl" version="2.0.0"/>

<!--服务的实现-->
<bean id="userServiceImpl" class="cn.ideal.mall.service.impl.UserServiceImpl"/>

老版本服务消费者配置:

<dubbo:reference id="userService" interface="cn.ideal.mall.service.UserService version="1.0.0" />

新版本服务消费者配置:

<dubbo:reference id="userService" interface="cn.ideal.mall.service.UserService version="2.0.0" />

如果不需要区分版本,可以按照以下的方式配置(2.2.0 以上版本支持)

<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />

三 Dubbo 高可用

这一块的讲解主要是针对,在一些突发的错误,或者大并发下等如何保证 Dubbo 仍为高可用状态的一些概念,以及措施

注:高可用,即通过设计,减少系统不能提供服务的时间

(一) zookeeper 宕机 和 Dubbo 直连

(1) zookeeper 宕机

zookeeper 作为注册中心,如果部署运行着它的服务器出问题了,出现了 zookeeper 宕机,那么这个时候消费者岂不是找不到被暴露的服务了

但是我们主动关掉 zookeeper 的服务,在dubbo-admin 监控中可以看到服务出现了错误,但是我们去请求接口,可以发现仍然能请求得到结果,即,还可以消费 dubbo 暴露的服务

这一点与 Dubbo 设计时实现的健壮性有关

  • 监控中心宕掉不影响使用,只是丢失部分采样数据

  • 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

  • 注册中心对等集群,任意一台宕掉后,将自动切换到另一台

  • 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

  • 服务提供者无状态,任意一台宕掉后,不影响使用

  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
    高可用:通过设计,减少系统不能提供服务的时间

https://dubbo.apache.org/zh/docs/v2.7/dev/principals/robustness/#m-zhdocsv27devprincipalsrobustness

Dubbo 的服务注册中心

目前服务注册中心使用了数据库来保存服务提供者和消费者的信息。注册中心集群不同注册中心也通过数据库来进行同步数据,以感知其它注册中心上提供者的变化。注册中心会在内存中保存一份提供者和消费者数据,数据库不可用时,注册中心独立对外提供服务以保证正常运转,只是拿不到其它注册中心的数据。当数据库恢复时,重试逻辑会将内存中修改的数据写回数据库,并拿到数据库中新数据。

服务的消费者

服务消费者从注册中心拿到提供者列表后,会保存提供者列表到内存和磁盘文件中。这样注册中心宕机后消费者可以正常运转,甚至可以在注册中心宕机过程中重启消费者。消费者启动时,发现注册中心不可用,会读取保存在磁盘文件中提供者列表。重试逻辑保证注册中心恢复后,更新信息。

(2) Dubbo 直连

如果注册中心现在有点问题,或者有意的不想访问注册中心上的服务,而是想要直接在本地上调试 dubbo 接口,也可以使用 Dubbo 直连

  • 第一种:通过 @Reference 的 url 属性(Springboot)

@Reference(url = "127.0.0.1:20081")
public UserService userService;
  • 第二种:在 consumer.xml 中指定 url (SSM)

<dubbo:reference interface="cn.ideal.mall.service.UserService" id="userService"  url="127.0.0.1:20081">
  • 第三种:添加映射配置文件

  • 如果不想要修改工程内容,可以考虑使用这个方式

在本地电脑用户下新建一个叫 dubbo-resolve.properties 的文件路径是${user.home}/dubbo-resolve.properties

然后就不需要修改本地工程的其他配置信息,在文件里配置好需要直连的服务信息即可

# 直连本地的服务
cn.ideal.mall.service.UserService=dubbo://localhost:20890

(二) 集群下 Dubbo 的负载均衡策略

这一块,我们只对几种负载均衡策略做一些说明和解释,具体的算法实现,不是几句话能说的清楚地,需要再深入的去学习以及研究,以下是官网关于负载均衡详细的说明

https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/#m-zhdocsv27devsourceloadbalance

(1) 什么是负载均衡

LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况。

通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载。在为高负载服务器分流的同时,还可以避免资源浪费,一举两得。

负载均衡可分为软件负载均衡和硬件负载均衡。在我们日常开发中,一般很难接触到硬件负载均衡。但软件负载均衡还是可以接触到的,比如 Nginx。

在 Dubbo 中,也有负载均衡的概念和相应的实现。Dubbo 需要对服务消费者的调用请求进行分配,避免少数服务提供者负载过大。服务提供者负载过大,会导致部分请求超时。因此将负载均衡到每个服务提供者上,是非常必要的。

Dubbo 提供了4种负载均衡实现,分别是

  • 基于权重随机算法的 Random LoadBalance

  • 以及基于加权轮询算法的 RoundRobin LoadBalance

  • 基于最少活跃调用数算法的 LeastActive LoadBalance

  • 基于 hash 一致性的 ConsistentHash LoadBalance

(2) 四种负载均衡策略

A:Random LoadBalance

基于权重随机算法的 RandomLoadBalance

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

随机算法,按权重设置随机概率

在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重

根据权重的这个比重来决定究竟用哪个

注:weight 代表权重,即在所有份数中所占的比例

B:RoundRobin LoadBalance

RoundRobin LoadBalance 基于权重的轮询负载均衡机制

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

如果只考虑轮轮询的意思就是,比如访问 1 --> 2 --> 3,再一轮又是 1 --> 2 --> 3,但是如果还要基于权重,是这样的,比如第一次是 1 --> 2 --> 3 的顺序,然后第二轮 1 --> 2 ,当应该到 3  时候,按照这个权重比例,总共看成 7 份机会,1 和 2 调用了两次了,各自占据了 2份,但是 3 服务应该只能占据 1份,所以只能跳过3了,再第三轮,1 已经两份了,所以也不应该用了,所以考虑取去 2,所以这几轮的顺序就是 1 --> 2 --> 3 -->1 --> 2 --> 2 --> 2

缺点:存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上

C:LeastActiveLoad Balance

LeastActive LoadBalance最少活跃数负载均衡机制

【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架

活跃数指调用前后计数差,计算活跃数使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大

注:如果活跃数相同,就随机

D:ConsistentHash LoadBalance

ConsistentHash LoadBalance一致性hash 负载均衡机制

这个算法会对,方法调用的第一个参数进行 Hash,例如就是对上面的 param 参数后面的 1、2、3 进行哈希,一致性 Hash,相同参数的请求总是发到同一提供者

好处就是,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动

(三) 整合hystrix,服务熔断与降级处理

(1) 服务降级

当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作

可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略

向注册中心写入动态配置覆盖规则:

RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));

其中:

  • mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响

  • 还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响

(2) 集群容错

在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试

A:集群容错模式

Failover Cluster

失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

重试次数配置如下:

<dubbo:service retries="2" />

<dubbo:reference retries="2" />

<dubbo:reference>
    <dubbo:method name="findFoo" retries="2" />
</dubbo:reference>

Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

Failback Cluster

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

Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

B:集群模式配置(2.1.0 开始支持)

按照以下示例在服务提供方和消费方配置集群模式,缺省为 failover 重试

<dubbo:service cluster="模式名" />

<dubbo:reference cluster="模式名" />

(四) 整合 Hystrix

在微服务架构中存在多个可直接调用的服务,这些服务若在调用时出现故障会导致连锁效应,也就是可能会让整个系统变得不可用,这种情况我们称之为服务雪崩效应,在这种时候,就需要我们的熔断机制来挽救整个系统

在微服务架构下,很多服务都相互依赖,如果不能对依赖的服务进行隔离,那么服务本身也有可能发生故障, Hystrix通过Hystrix Command对调用进行隔离, 这样可以阻止故障的连锁反应,能够让接口调用快速失败并迅速恢复正常,或者回退并优雅降级

关于 Hystrix ,会在 SpringCloud 的学习文章整理中进行介绍,这里只做一个简单的使用

首先引入依赖

注意,注意!如果拟引入的 hystrix 版本相对较新,或许会报错

Error creating bean with name 'configurationPropertiesBeans…….

一种情况就是你的 springboot 版本太新了,需要降低一下,spring 官网可以看到 现在的 cloud 对应能支持到哪个版本的 boot

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

启动类添加 @EnableHystrix 注解

@EnableDubbo // 开启基于注解的dubbo功能
@EnableHystrix //开启服务容错功能
@SpringBootApplication
public class BootUserServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootUserServiceProviderApplication.class, args);
    }
}

在提供者中添加注解

可以直接使用 @HystrixCommand,后面都是一些属性设置,下面的随机数判断是为了模拟异常

@Service
@Component
public class UserServiceImpl implements UserService {

    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
//    @HystrixCommand
    public List<UserAddress> getUserAddressList(String userId) {

        UserAddress address1 = new UserAddress(1"广东省xxx市xxx区xxx路xxx小区24栋1单元801户""1""阿文""13999999999""Y");
        UserAddress address2 = new UserAddress(2"北京市yyy区yyy路yyy小区19号3单元502户""1""北方少女的梦""13888888888""N");
        if (Math.random() > 0.5){
            throw new RuntimeException();
        }
        return Arrays.asList(address1,address2);
    }
}

在消费者中添加注解

method上配置@HystrixCommand。当调用出错时,会走 fallbackMethod = "testError"

@Service
public class OrderServiceImpl implements OrderService {

    @Reference
    public UserService userService;

    @HystrixCommand(fallbackMethod = "testError")
    public List<UserAddress> initOrder(String userID) {

        //查询用户的收货地址
        List<UserAddress> userAddressList = userService.getUserAddressList(userID);

        //为了直观的看到得到的数据,以下内容也可不写
        System.out.println("当前接收到的userId=> "+userID);
        System.out.println("**********");
        System.out.println("查询到的所有地址为:");
        for (UserAddress userAddress : userAddressList) {
            //打印远程服务地址的信息
            System.out.println(userAddress.getUserAddress());
        }
        return userAddressList;
    }

    public List<UserAddress> testError(){
        return Arrays.asList(new UserAddress(10,"错误测试地址",
                "1","测试BWH","15555555555","N"));
    }
}

结尾

邮箱:ideal_bwh@163.com

如果帮到你的话,那就来关注我吧!

如果您更加喜欢PC端的阅读方式,可以访问我的个人博客

域名:www.ideal-20.cn

在这里的我们素不相识,却都在为了自己的梦而努力 ❤

以上是关于万字长文Dubbo 入门总结 ,一款高性能的 Java RPC 框架的主要内容,如果未能解决你的问题,请参考以下文章

java基础入门黑马程序员第二版,万字长文!

万字长文入门 Redis 命令事务锁订阅性能测试

万字长文!总结 Vue 性能优化方式及原理(收藏)

记录一下Dubbo的快速入门

接口调试神器:Postman 从入门到进阶教程(万字长文)!

Dubbo系列讲解之服务发现3万字长文分享