dubbo

Posted 穷少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo相关的知识,希望对你有一定的参考价值。

文章目录

dubbo是什么?

  1. Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。
  2. Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

Dubbo 架构


上述节点简单说明:

  • Provider 暴露服务的服务提供方
  • Consumer 调用远程服务的服务消费方
  • Registry 服务注册与发现的注册中心
  • Monitor 统计服务的调用次数和调用时间的监控中心
  • Container 服务运行容器

调用关系说明: 1. 服务容器负责启动,加载,运行服务提供者。 2. 服务提供者在启动时,向注册中心注册自己提供的服务。 3. 服务消费者在启动时,向注册中心订阅自己所需的服务。 4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。 5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

我们在讲 Dubbo 的时候提到了 Dubbo 实际上是一款 RPC 框架,那么RPC 究竟是什么呢?相信看了下面我对 RPC 的介绍你就明白了!

什么是 RPC?

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务A,B部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好。 RPC 的出现就是为了解决这个问题。

dubbo官方中文文档

https://dubbo.apache.org/zh/

dubbo-admin的下载安装

dubbo-admin 是dubbo的管理界面平台,且是一个前后端分离的项目,前端使用vue,后端使用springboot。

  1. 下载
    dubbo-admin是在github上发布的项目源码,所以我只需要前往github下载即可
    github地址:https://github.com/apache/dubbo-admin

  2. 运行项目

    • dubbo-admin是一个前后端分离项目,所以我们需要分别启动后端服务器和前端程序
    • 在启动后端服务之前,需要在在 dubbo-admin-server/src/main/resources/application.properties中指定注册中心地址(注册中心一般推荐使用zookeeper)
    • 前端项目启动:进入dubbo-admin-ui文件夹中,运行npm run dev即可启动


  3. 启动成功访问给出的访问地址
    密码用户名默认都是root

dubbo+springboot快速入门

以前我们的项目全部代码都是放到同一台服务器上运行的单机模式,现在系统模块拆分,并部署到各个服务器上运行,并使用dubbo来作为服务中介

1.使用zookeeper来作为注册中心

如何使用zookeeper具体请看这篇文章:zookeeper的安装与使用

2.搭建公共接口(API)模块

公共模块里面,存放接口,与实体类(需要实现序列化),采用maven项目实现,以便后续项目的使用

DemoService

public interface DemoService 

    public String sayHello();

    public User getUser();


User
因为dubbo是一个远程服务调用框架,在服务之前调用,要想传输类对象,这个实体类就必须实现序列化(Serializable ),这也是dubbo一大高级特性

public class User implements Serializable 

    private Integer id;
    private String name;
    private Double score;

    public User() 
    

    public User(Integer id, String name, Double score) 
        this.id = id;
        this.name = name;
        this.score = score;
    

    public Integer getId() 
        return id;
    

    public void setId(Integer id) 
        this.id = id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Double getScore() 
        return score;
    

    public void setScore(Double score) 
        this.score = score;
    

使用 mvn install命令将这个项目安装到本地的maven仓库

3.搭建服务提供者(provider)

  1. 创建springboot项目,取名为provider
  2. 导入相应的坐标
<dependencies>
		<!--将第一步创建的公共模块导入进来-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>api_simple</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--spring boot starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.5.2</version>
        </dependency>
		<!--dubbo springboot集成依赖-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>
        <!--Curator是一个比较完善的ZooKeeper客户端框架,通过封装的一套高级API 简化了ZooKeeper的操作-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>5.1.0</version>
            <exclusions>
            	<!--因为springboot本身自带sl4j日志,所以我们不需要log4j-->
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
  1. 编写application.properties文件
# 告诉注册中心,你的项目的名称,具有唯一性
dubbo.application.name=provider
# 告诉程序你的注册中心的地址,端口号,以及所使用的的协议,我们使用zookeeper作为注册中心,那么协议就是zookeeper
dubbo.registry.address=zookeeper://192.168.23.135:2181
# 下面这个配置使用为了防止,在第一次注册的时候,由于timeout时间过短导致的连接失败的情况
dubbo.config-center.timeout=10000
dubbo.registry.timeout=10000
# 因为是服务端,我们需要让程序像服务器一样运行起来,那么我们就必须要给程序一个端口号
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

 # 元数据配置,注册中心地址 如果不配则无法看见元数据 
 # 但是个人发现,这个配置写上去,就会报zookeeper not connected异常
 # dubbo-springboot版本2.7.8不配置这一项,元数据一样会显示
# dubbo.metadata-report.address=zookeeper://127.0.0.1:2181
  1. 继承实现公共模块的接口,实现接口中的方法
// 1. 以前这里的注解是@Service 
// 2. 作用是将UserServiceImpl生成bean对象并放入spring容器
// 3. @DubboService是将此服务信息(ip:port、服务名字),放在注册中心,提供给消费者。
@DubboService(interfaceClass = DemoService.class)
public class DemoServiceImpl implements DemoService 

    @Override
    public String sayHello() 
        return "hello world";
    

    @Override
    public User getUser() 
        return new User(1,"zhangsan",99.9);
    


  1. 在springboot的启动类上添加@EnableDubbo注解,表示启动dubbo服务,如下:
  2. 打开dubbo Admin查看。

    点击详情,可以具体看见详细信息,如ip、port、权重等等。
    继续往下看,则可以看见元数据,是指服务里面具体的方法名,参数列表,返回值。

4.搭建服务消费者(consumer)

  1. 创建springboot工程,取名为consumer
  2. 导入相应jar包坐标。
<dependencies>
		<!--公共模块的坐标-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>api_simple</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--spring boot starter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.5.2</version>
        </dependency>
        <!--因为是消费者,调用者,所以我们的项目是一个web项目-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.5.2</version>
        </dependency>
        <!--dubbo springboot集成依赖-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>
        <!--Curator是一个比较完善的ZooKeeper客户端框架,通过封装的一套高级API 简化了ZooKeeper的操作-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>5.1.0</version>
            <exclusions>
            	<!--因为springboot本身自带sl4j日志,所以我们不需要log4j-->
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
  1. 编写application.properties属性文件
# 告诉注册中心,你的项目的名称,具有唯一性
dubbo.application.name=provider
# 告诉程序你的注册中心的地址,端口号,以及所使用的的协议,我们使用zookeeper作为注册中心,那么协议就是zookeeper
dubbo.registry.address=zookeeper://192.168.23.135:2181
# 下面这个配置使用为了防止,在第一次注册的时候,由于timeout时间过短导致的连接失败的情况
dubbo.config-center.timeout=10000
dubbo.registry.timeout=10000
# web项目的端口号
server.port=8888

4.编写web层

@RestController
public class DemoController 
	
	// DubboReference的作用是去注册中心寻找对应的服务,
	// 并且生成代理对象,放入spring容器中,并赋值给demoService
    @DubboReference
    private DemoService demoService;

    @RequestMapping("demo01")
    public Object demo01()

        return demoService.sayHello();
    

    @RequestMapping("demo02")
    public Object demo02()

        return demoService.getUser();
    



  1. 同理,在springboot的启动类上添加@EnableDubbo注解,表示启动dubbo服务。
  2. 访问两个接口


到这里,我们的分布式小项目基本就完成了

5.源码地址

https://gitee.com/cn2770345524/test_dubbo02/

参考文章:

  1. SpringBoot整合Dubbo
  2. 超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务
  3. Dubbo(一)-----基础知识、最详细的springboot整合dubbo

dubbo的高级特性

1.对象序列化

dubbo框架是一个远程调用过程的框架,允许我们通过网络传输对象,但是传输的对象必须要实现Serializable接口

2.地址缓存

有这么一种情况,我们所调用的服务都是正常的,但是服务中心(registry)挂掉了,这个时候我们还能不能正常访问服务呢?

是可以的。dubbo框架中,当服务都是正常运行的,但是注册中心(registry)挂掉了之后,依然允许我们正常的调用原有所使用的的服务

在consumer调用了某种服务后,dubbo框架会自动的将该服务的地址,协议等相关信息缓存到本地,虽然注册中心挂掉了,但是只要原有服务的地址没变,依然可以访问服务,这就是dubbo的地址缓存

3.超时机制

在生产环境中,我们通常会遇到这种情况,调用某种服务时,因为服务提供方的系统资源不够,导致服务执行耗时过长,导致整个程序过程就卡在了调用服务哪里

dubbo提供了一种超时机制,给服务设置一个超时限制,当服务调用超过了最大时限,系统就认为不需要再等待,通知服务调用方服务超时

超时是针对消费端还是服务端?

  • 如果是针对消费端,那么当消费端发起一次请求后,如果在规定时间内未得到服务端的响应则直接返回超时异常,但服务端的代码依然在执行。

  • 如果是针对服务端,那么当消费端发起一次请求后,一直等待服务端的响应,服务端在方法执行到指定时间后如果未执行完,此时返回一个超时异常给到消费端。

  1. 给服务提供者设置超时时间
// 超时针对服务端,那么当消费端发起一次请求后,一直等待服务端的响应,服务端在方法执行到指定时间后如果未执行完,此时返回一个超时异常给到消费端
// dubbo默认超时设置为1000毫秒
@DubboService(interfaceClass = DemoService.class,timeout = 3000)
public class DemoServiceImpl implements DemoService 
	,,,,,

  1. 给服务调用者设置超时时间
@RestController
public class DemoController 
	
	// 超时针对消费端,那么当消费端发起一次请求后,如果在规定时间内未得到服务端的响应则直接返回超时异常,但服务端的代码依然在执行
    @DubboReference(timeout = 3000)
    private DemoService demoService;
,,,,,

4.重试机制

在调用远程服务时,可能会因为网络问题,或者服务调用超时问题导致服务调用失败,dubbo框架提供了一种重试机制

当服务调用超时/调用失败之后,会重新尝试再次调用服务,如果调用服务的次数超过设置的最大重试次数后,就会认为服务调用失败

// 通过retries 来设置服务重试的最大次数
// 例如 当服务调用超时后,dubbo会再次调用服务,最多会重试三次
@DubboService(interfaceClass = DemoService.class,timeout = 3000,retries = 3)
public class DemoServiceImpl implements DemoService 
	,,,,,

5.多版本

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

在生产环境中,我们的软件需要经过多个版本的迭代更新,一般我们采用灰度更新的方式更新,即:先让一部分用户使用新版本服务,用户反馈没有问题,我们再面向全部用户推广

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

  • 在低压力时间段,先升级一半提供者为新版本
  • 再将所有消费者升级为新版本
  • 然后将剩下的一半提供者升级为新版本

服务端

// 通过version设置服务版本信息
// 一种服务可以有多个版本,一个服务可以有多个服务者提供
@DubboService(version = "1.0")
public class DemoServiceImpl implements DemoService 
	,,,,,

// 通过version设置服务版本信息
// 一种服务可以有多个版本,一个服务可以有多个服务者提供
@DubboService(version = "2.0")
public class DemoServiceImpl implements DemoService 
	,,,,,

消费端

@RestController
public class DemoController 
	
	// 通过version指定调用服务的哪个版本
    @DubboReference(version = "1.0")
    private DemoService demoService;



6.负载均衡

在生产环境中,我们通常是由集群的方式来提供服务的,此时就涉及到负载均衡的问题了
以怎么的方式来访问集群里面的机器,dubbo给我们提供一下几种策略

策略作用
random(默认策略)随机,按权重设置随机概率;在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重
roundrobin轮询,按公约后的权重设置轮询比率。存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上
leastactive最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差;每个服务维护一个活跃数计数器。当A机器开始处理请求,该计数器加1,此时A还未处理完成。若处理完毕则计数器减1。而B机器接受到请求后很快处理完毕。那么A,B的活跃数分别是1,0。当又产生了一个新的请求,则选择B机器去执行(B活跃数最小),这样使慢的机器A收到少的请求。
consistenthash一致性 Hash,相同参数的请求总是发到同一提供者.当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动

在负载均衡中,是根据服务者权重(weight)来实行上述的策略,我们可以手动给服务设置策略与权重

所有的策略名称都在与之相对应的类里面

通过给服务来weight设置服务的权重,通过设置loadbalance来设置服务的负载均衡策略

// loadbalance既可以在服务端设定,也可以在消费方设置
@DubboService(weight = 100,loadbalance = "random")
public class DemoServiceImpl implements DemoService 
	......

@DubboReference(loadbalance = "random")
private DemoService demoService;

负载均衡只有在集群模式下才会生效

7.集群容错

在调用服务的时候,可能会出现调用服务失败的情况,在集群模式下,如何应对这种情况呢
dubbo为我们提供了一下几种集群容错的方案:

方案作用
Failover Cluster(默认方案)失败重试。当出现失败时,重试其他服务器,默认重试2次,使用retrires配置,一般用于读操作
Failfast Cluster快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录
Failsafe Cluster失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作
Failback Cluster失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
Forking Cluster并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数
Broadcast Cluster广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。broadcast.fail.percent=20 代表了当 20% 的节点调用失败就抛出异常,不再调用其他节点
// 既可以针对消费方,也可以针对服务方

// 消费方
@DubboReference(cluster = "failover",retries = 2)

// 服务方
@DubboService(cluster = "failover")

具体的设置参数,可以参照官网API

8.服务降级

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

失败返回null值,但是不报错

@DubboReference(version = "1.0",mock = "fail:return null")
 private DemoService demoService;

无论服务是否正常运行,直接不去访问,返回null

@DubboReference(version = "1.0",mock = "force:return null")
 private DemoService demoService;

以上是关于dubbo的主要内容,如果未能解决你的问题,请参考以下文章

dubbo面试题!会这些,说明你真正看懂了dubbo源码

dubbo面试题,会这些说明你真正看懂了dubbo源码

一道Dubbo高频面试题,请查收

Dubbo-admin无法显示Group分组信息

dubbo 的注册中心挂了可以继续通信吗?

jmeter dubbo测试