Dubbo由浅入浅

156 阅读5分钟

1.Dubbo是啥

Dubbo是一款基于Java的RPC(Remote Procedure Call )框架。使用Dubbo框架可以实现两个远程服务之间的通信。同时,Dubbo还是一款微服务开发框架。除了提供RPC通信功能以外,还提供了一系列微服务治理能力,例如,服务发现、负载均衡、服务降级等。

2.Dubbo工作流程

Dubbo工作流程如下图所示:

image-20211017153258837.png

Dubbo的工作过程主要涉及到四个地方,服务的消费者(Consumer)服务的提供者(Provider)服务的注册中心(Registry)监控中心(Monitor)

  1. 当服务提供者(Provider)启动的时候,会将自身的服务信息注册到注册中心
  2. 服务消费者(Consumer)会从注册中心订阅服务提供者的信息,将订阅的服务提供者信息缓存起来,以便当注册中心挂掉之后还可以进行正常的通信
  3. 服务消费者和服务提供者以异步的方式发送消息到监控中心,开发者即可通过监控中心来可视化监控各个服务的状态情况。

3.Dubbo的三大组件

为了实现在分布式环境中各个微服务组件之间的通信协作,Dubbo定义了一些重要的组件:

  • 注册中心
    • 负责Consumer与Provider之间的注册和发现。
  • ?配置中心
    • 存储Dubbo启动阶段的全局配置,保证配置的跨环境共享和全局一致性。
    • 负责服务治理规则(路由规则、动态配置)的存储和推送。
  • 元数据中心
    • 接收Provider上报的服务接口元数据,为Admin等控制台提供运维能力
    • 作为服务发现机制的补充,提供额外的配置信息的同步能力

image-20211017181002825.png

需要注意的是,以上三个中心并不是运行Dubbo的必要条件,用户完全可以根据自身业务情况决定只启动其中一个或多个。

注册中心: image-20211017181240915.png

配置中心:

image-20211017181303561.png

元数据中心:

image-20211017181325605.png

4.注册中心搭建

注册中心是Dubbo实现服务发现的关键组件,通常注册中心都是使用第三方的注册中心组件,例如Zookeeper、Nacos。Dubbo推荐使用Zookeeper,Zookeeper真正发挥作用的地方其实是在大数据领域,仅仅是用来当做Zookeeper似乎有点大材小用。而Nacos则是阿里巴巴开源的注册中心组件,也是比较常用的。

在这里我将使用Zookeeper作为注册中心,并且为了方便我将在Windows上安装。

参考博文:blog.csdn.net/qq_33316784…

5.Dubbo Admin搭建

blog.csdn.net/zhanggongla…

参考博文:Dubbo Admin控制台可以帮助我们可视化的管理我们的服务。

6.服务发现与服务调用

我这里使用的是SpringBoot和Dubbo整合。

工程目录如下:

--dubbo3-demo

-------dubbo-demo-api

-------dubbo-demo-comsumer

-------dubbo-demo-provider

目前使用的Dubbo版本是2.7.8。

父工程pom.xml

<?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 http://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.0.6.RELEASE</version>
    </parent>
    <groupId>com.qingyuan</groupId>
    <artifactId>dubbo3-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <dubbo.version>2.7.8</dubbo.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.0.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <version>2.0.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
            <!-- Zookeeper dependencies -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-zookeeper</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <exclusions>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>

        </dependencies>
    </dependencyManagement>
</project>

dubbo-demo-api:

<?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>com.qingyuan</groupId>
        <artifactId>dubbo3-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>com.qingyuan</groupId>
    <artifactId>dubbo-demo-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-demo-api</name>
    <description>dubbo-demo-api</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

</project>

新建ProviderService,这里要注意的是,服务消费者和服务提供者注册和引用的Service接口必须是同一个才能引用成功:

public interface ProviderService {
    public String helloworld();
}

服务提供者dubbo-demo-provider pom.xml

<?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>com.qingyuan</groupId>
        <artifactId>dubbo3-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>com.qingyuan</groupId>
    <artifactId>dubbo3-demo-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo3-demo-provider</name>
    <description>dubbo3-demo-provider</description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.qingyuan</groupId>
            <artifactId>dubbo-demo-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>

appilication.properties:

# 服务启动端口
server.port=8082


# dubbo应用名
dubbo.application.name=dubbo-demo-provider
## 将扫描指定包下带有@DubboService标签的Service注册到注册中心
dubbo.scan.base-packages=com.qingyuan.dubbo3demoprovider
# dubbo协议端口20880
dubbo.protocol.port=20880
# dubbo协议名
dubbo.protocol.name=dubbo
# 注册中心zookeeper
dubbo.registry.address=zookeeper://localhost:2181

dubbo.config-center.timeout=10000

## DemoService version
demo.service.version=1.0.0

编写Provider实现类:

@DubboService(version = "${demo.service.version}")
public class ProviderServiceImpl implements ProviderService {
    @Override
    public String helloworld() {
        return "helloworld";
    }
}

启动类:

@SpringBootApplication
public class Dubbo3DemoProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(Dubbo3DemoProviderApplication.class, args);
    }

}

启动应用,然后就可以在dubbo admin中看到我们注册进去的服务:

image-20211017182640067.png

服务消费者dubbo-demo-consumer:

pom.xml:

<?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>com.qingyuan</groupId>
        <artifactId>dubbo3-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>com.qingyuan</groupId>
    <artifactId>dubbo3-demo-comsumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo3-demo-comsumer</name>
    <description>dubbo3-demo-comsumer</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <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>com.qingyuan</groupId>
            <artifactId>dubbo-demo-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

</project>

application.properties:

server.port= 8081dubbo.application.name=dubbo-demo-consumer
dubbo.registry.address=zookeeper://localhost:2181
## DemoService version
demo.service.version=1.0.0

新建消费者Controller用于测试服务接口调用:

@Component
@RequestMapping("/comsumer")
public class ConsumerController {

    /**
     * 注入服务提供方的服务
     */
    @DubboReference(version = "${demo.service.version}")
    private ProviderService service;


    @GetMapping("/helloworld")
    @ResponseBody
    public String get() {
        return service.helloworld();
    }
}

启动消费者之后调用接口:

image-20211024182327850.png

7.启动时检查

在Dubbo中,服务消费者启动的时候默认会去检查是否能在注册中心找到需要的服务提供者的信息,如果找不到,就会抛出异常。

解决方案:

1.启动消费者之前需要先启动服务提供者

2.通过配置文件关闭某个服务的启动前检查

## 覆盖 com.foo.BarService的 reference 的 check 值
dubbo.reference.com.foo.BarService.check=false

## 设置reference的 check 的缺省值
dubbo.consumer.check=false

## 设置reference的 check 的缺省值
dubbo.registry.check=false

8.负载均衡

8.1 Dubbo提供的负载均衡算法

Dubbo提供了多种负载均衡策略。Consumer通过负载均衡算法得出需要调用哪一个具体的Provider实例。

算法特性备注
RandomLoadBalance加权随机默认算法
RoundRobinLoadBalance加权轮询
LeastActiveLoadBalance最少活跃优先+加权随机能者多劳
ShortestResponseLoadBalance最短响应优先 + 加权随机更加关注响应速度
ConsistentHashLoadBalance一致性Hash确定的入参,确定的提供者

负载均衡的目的是为了在特定场景下,能够将请求合理地分配到各个服务实例上。

  • RandomLoadBalance。随机负载均衡。

            if (totalWeight > 0 && !sameWeight) {
                // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
                int offset = ThreadLocalRandom.current().nextInt(totalWeight);
                // Return a invoker based on the random value.
                for (int i = 0; i < length; i++) {
                    offset -= weights[i];
                    if (offset < 0) {
                        return invokers.get(i);
                    }
                }
            }
    

8.1.2 设置负载均衡的优先级

Dubbo支持在消费者和提供者进行负载均衡的设置。其中的优先级关系为:

  1. 消费者方法级别配置
  2. 消费者接口级别配置
  3. 提供者方法级别配置
  4. 提供者接口级别配置