SpringCloud Alibaba (四):Dubbo RPC框架

小高飛發表於2020-07-05

Dubbo簡介

Apache Dubbo |ˈdʌbəʊ| 是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。致力於提高效能和透明化的RPC遠端服務呼叫方案,以及SOA服務治理方案。

SpringCloud Alibaba (四):Dubbo RPC框架

節點角色說明
Provider 暴露服務的服務提供方
Consumer 呼叫遠端服務的服務消費方
Registry 服務註冊與發現的註冊中心
Monitor 統計服務的呼叫次數和呼叫時間的監控中心
Container 服務執行容器

功能特點:

  • 面向介面代理的高效能RPC呼叫

    提供高效能的基於代理的遠端呼叫能力,服務以介面為粒度,為開發者遮蔽遠端呼叫底層細節。

  • 智慧負載均衡

    內建多種負載均衡策略,智慧感知下游節點健康狀況,顯著減少呼叫延遲,提高系統吞吐量。

  • 服務自動註冊與發現

    支援多種註冊中心服務,服務例項上下線實時感知。

  • 高度可擴充套件能力

    遵循微核心+外掛的設計原則,所有核心能力如Protocol、Transport、Serialization被設計為擴充套件點,平等對待內建實現和第三方實現。

  • 執行期流量排程

    內建條件、指令碼等路由策略,通過配置不同的路由規則,輕鬆實現灰度釋出,同機房優先等功能。

  • 視覺化的服務治理與運維

    提供豐富服務治理、運維工具:隨時查詢服務後設資料、服務健康狀態及呼叫統計,實時下發路由策略、調整配置引數。

Spring Cloud 為什麼需要RPC

在Spring Cloud構建的微服務系統中,大多數的開發者使用都是官方提供的Feign元件來進行內部服務通訊,這種宣告式的HTTP客戶端使用起來非常的簡潔、方便、優雅,但是有一點,在使用Feign消費服務的時候,相比較Dubbo這種RPC框架而言,效能堪憂。

雖說在微服務架構中,會講按照業務劃分的微服務獨立部署,並且執行在各自的程式中。微服務之間的通訊更加傾向於使用HTTP這種簡答的通訊機制,大多數情況都會使用REST API。這種通訊方式非常的簡潔高效,並且和開發平臺、語言無關,但是通常情況下,HTTP並不會開啟KeepAlive功能,即當前連線為短連線,短連線的缺點是每次請求都需要建立TCP連線,這使得其效率變的相當低下。

對外部提供REST API服務是一件非常好的事情,但是如果內部呼叫也是使用HTTP呼叫方式,就會顯得顯得效能低下,Spring Cloud預設使用的Feign元件進行內部服務呼叫就是使用的HTTP協議進行呼叫,這時,我們如果內部服務使用RPC呼叫,對外使用REST API,將會是一個非常不錯的選擇,恰巧,Dubbo Spring Cloud給了我們這種選擇的實現方式。

 

SpringCloud Alibaba 整合 Dubbo

建立ApacheDubbo總工程, 在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><groupId>com.gofy</groupId>
    <artifactId>ApacheDubbo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging><parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
    </parent><modules>
        <module>dubbo-provider</module>
        <module>dubbo-consumer</module>
    </modules><properties>
        <!-- Environment Settings -->
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
        <spring-cloud-alibaba.version>0.2.1.RELEASE</spring-cloud-alibaba.version>
        <dubbo.version>2.7.6</dubbo.version>
        <dubbo-spring.version>2.7.6</dubbo-spring.version>
        <dubbo-actuator.version>2.7.6</dubbo-actuator.version>
        <dubbo-kryo.version>2.7.6</dubbo-kryo.version>
        <dubbo-nacos.version>2.7.6</dubbo-nacos.version>
        <dubbo-nacos-config.version>2.1.0.RELEASE</dubbo-nacos-config.version>
        <spring-context-support.version>1.0.6</spring-context-support.version>
    </properties><dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency><!-- Apache Dubbo  -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-actuator</artifactId>
                <version>${dubbo-actuator.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo-spring.version}</version>
            </dependency>
            <!-- 使用kryo序列化/反序列化工具, 提高專案效能 -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-serialization-kryo</artifactId>
                <version>${dubbo-kryo.version}</version>
            </dependency><!-- Spring Cloud Alibaba -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>dubbo-registry-nacos</artifactId>
                <version>${dubbo-nacos.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.nacos</groupId>
                <artifactId>nacos-client</artifactId>
                <version>1.2.1</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
                <version>${dubbo-nacos-config.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.spring</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${spring-context-support.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement><build>
        <plugins>
            <!-- Compiler 外掛, 設定 JDK 版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>
        </plugins><!-- 資原始檔配置 -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>
</project>

服務提供者

在Dubbo RPC框架中, 服務提供者的介面類和實現類應該分開為倆個模組, 所以我們應該在服務提供者下建立兩個子模組, 分別為 介面模組dubbo-provider-api實現模組dubbo-provider-service

在總工程 ApacheDubbo下建立dubbo-provider模組, 新增服務提供者的統一依賴

<?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>
        <artifactId>ApacheDubbo</artifactId>
        <groupId>com.gofy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.gofy</groupId>
    <artifactId>dubbo-provider</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>dubbo-provider-api</module>
        <module>dubbo-provider-service</module>
    </modules>
</project>
  • dubbo-provider下建立dubbo-provider-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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>dubbo-provider</artifactId>
        <groupId>com.gofy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent><groupId>com.gofy</groupId>
    <artifactId>dubbo-provider-api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging></project>

建立介面類 EchoService

package com.gofy.dubbo.api;
​
public interface EchoService {
    String echo(String s);
}
  • dubbo-provider下建立dubbo-provider-service子模組, 並新增依賴

匯入介面模組失敗原因: 一般是總工程的統一依賴出了問題, 可以檢視本地maven倉庫的中總工程匯入的依賴的包有沒有缺失檔案. 我之前失敗原因就是 spring-cloud-dependencies 包出了問題, Greenwich.SR5版本下載一直缺失檔案, 改為Greenwich.SR3就好了.

<?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>
        <artifactId>dubbo-provider</artifactId>
        <groupId>com.gofy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.gofy</groupId>
    <artifactId>dubbo-provider-service</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <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>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-serialization-kryo</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.spring</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <!-- 匯入介面模組 -->
        <dependency>
            <groupId>com.gofy</groupId>
            <artifactId>dubbo-provider-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.gofy.dubbo.ProviderApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

dubbo-provider-service的application.yml裡新增配置

注:如果要在 SpringClou Alibaba+Dubbo 中使用nacos動態配置,操作與之前naocs動態配置的操作一樣

spring:
  application:
    name: dubbo-provider
  main:
    allow-bean-definition-overriding: true # 解決bean重複定義,設定為true時,後定義的bean會覆蓋之前定義的相同名稱的bean

dubbo:
  scan:
    base-packages: com.gofy.dubbo.service # 實現類所在的包
  protocol:
    name: dubbo
    port: -1 # 埠為-1時,即是讓dubbo自動分配埠
    serialization: kryo # 使用kryo序列化/反序列化工具
  registry:
    address: nacos://192.168.11.132:8848 #註冊中心地址,格式為 註冊中心元件名://註冊中心訪問地址

建立實現類 EchoServiceImpl

package com.gofy.dubbo.service;
​
import com.gofy.dubbo.api.EchoService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Value;
​
@Service(version = "1.0.0") //服務版本號
public class EchoServiceImpl implements EchoService {
​
    @Override
    public String echo(String s) {
        return "Hello Dubbo "+s;
    }
}

建立啟動類 ProviderApplication

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

服務消費者

在總工程 ApacheDubbo下建立服務消費者 dubbo-consumer, 並新增依賴

<?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>
        <artifactId>ApacheDubbo</artifactId>
        <groupId>com.gofy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.gofy</groupId>
    <artifactId>dubbo-consumer</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- SpringBoot -->
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- Dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-serialization-kryo</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.spring</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <!-- 匯入介面模組 -->
        <dependency>
            <groupId>com.gofy</groupId>
            <artifactId>dubbo-provider-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.gofy.dubbo.ConsumerApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

dubbo-consumer的application.yml裡新增配置

server:
  port: 8080
​
spring:
  application:
    name: dubbo-consumer
  main:
    allow-bean-definition-overriding: true
​
dubbo:
  scan:
    base-packages: com.gofy.dubbo.controller #controller類所在包
  protocol:
    name: dubbo
    port: -1
    serialization: kryo
  registry:
    address: nacos://192.168.11.132:8848
​
endpoints:
  dubbo:
    enabled: true #允許暴露dubbo分配的端點
​
management:
  health: #健康檢查
    dubbo:
      status:
        defaults: memory
        extras: threadpool
  endpoints: #暴露所有web端點
    web:
      exposure:
        include: "*"

建立controller類 EchoController

package com.gofy.dubbo.controller;
​
import com.gofy.dubbo.api.EchoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
public class EchoController {
​
    @Reference(version = "1.0.0") //通過服務的版本號注入
    EchoService echoService;
    
    @GetMapping("/echo/{s}")
    public String echo(@PathVariable("s")String s){
        return echoService.echo(s);
    }
}

建立啟動類 ConsumerApplication

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

訪問測試

訪問nacos註冊中心,檢視已註冊服務

SpringCloud Alibaba (四):Dubbo RPC框架

訪問 localhost:8080/echo/hi , 成功獲取到服務提供者返回的資訊

SpringCloud Alibaba (四):Dubbo RPC框架

 

Dubbo負載均衡

當我們對內使用Dubbo的RPC通訊,不再使用RestTemplate或feign的 HTTP通訊時,我們要怎麼使用負載均衡呢?

在 Dubbo 中,也有負載均衡的概念和相應的實現。Dubbo 需要對服務消費者的呼叫請求進行分配,避免少數服務提供者負載過大。服務提供者負載過大,會導致部分請求超時。因此將負載均衡到每個服務提供者上,是非常必要的。Dubbo 提供了4種負載均衡實現,分別是基於權重隨機演算法的 RandomLoadBalance、基於最少活躍呼叫數演算法的 LeastActiveLoadBalance、基於 hash 一致性的 ConsistentHashLoadBalance,以及基於加權輪詢演算法的 RoundRobinLoadBalance。

原始碼分析

Dubbo負載均衡的原始碼在 org.apache.dubbo:dubbo下的org.apache.dubbo.rpc.cluster.loadbalance

通過原始碼可以發現4個負載均衡策略的實現類都繼承了AbstractLoadBalance抽象類,而AbstractLoadBalance實現了LoadBalance介面。

再來看看LoadBalance介面,可以知道duboo是通過 loadbalance屬性來適配負載均衡介面的實現類,且預設值為 random權重隨機。

@SPI("random")
public interface LoadBalance {
    @Adaptive({"loadbalance"})
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}

所以,我們只要在@Reference註解裡新增 loadbalance屬性,就可以選擇dubbo的負載均衡策略了

loadbalance屬性值為負載均衡實現類的 NAME屬性,分別是:

random 、roundrobin 、leastactive 、consistenthash

@Reference(version = "1.0.0", loadbalance = "roundrobin")
EchoService echoService;

負載均衡策略實現類的詳細原始碼分析,dubbo官方文件裡講解得非常好,就不多轉述了



我的個人部落格站

翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 谷歌翻譯(國內)

翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 谷歌翻譯(國內)

相關文章