1. 程式人生 > >分散式RPC系統框架Dubbo

分散式RPC系統框架Dubbo

導讀

  Apache Dubbo是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力;面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。

dubbo官網:點我直達

第一個Dubbo程式(小試牛刀)

建立業務介面工程

專案結構

建立包和介面類

安裝專案

建立提供者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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.cyb</groupId>
    <artifactId>02-first-provider</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--編譯器依賴-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
        <!--自定義版本號-->
        <spring-version>4.3.16.RELEASE</spring-version>
    </properties>
    <dependencies>
        <!--自定義工程依賴-->
        <dependency>
            <groupId>org.cyb</groupId>
            <artifactId>01-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--dubbo依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.7</version>
        </dependency>
        <!--netty-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.32.Final</version>
        </dependency>


        <!--spring依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring-version}</version>
        </dependency>
    </dependencies>
</project>

注意

spring-dubbo-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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--當前工程名稱,該名稱將在監控平臺使用-->
    <dubbo:application name="02-first-provider"/>
    <!--註冊Service,將來服務的提供者-->
    <bean id="someService" class="com.cyb.service.SomeServiceImpl"></bean>
    <!--暴露服務,採用直連的方式-->
    <dubbo:service interface="com.cyb.service.SomeService" ref="someService" registry="N/A"></dubbo:service>
</beans>

SomeServiceImpl.java

package com.cyb.service;

public class SomeServiceImpl implements SomeService{
    @Override
    public String hello(String name) {
        System.out.println("Dubbo World Welcome You"+name);
        return "chenyanbin";
    }
}

RunProvider.java

package com.cyb.run;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class RunProvider {
    public static void main(String[] args) throws IOException {
        //建立spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-dubbo-provider.xml");
        //啟動spring容器
        ((ClassPathXmlApplicationContext) ac).start();
        //將當前主執行緒阻塞
        System.in.read();
    }
}

建立消費者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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.cyb</groupId>
    <artifactId>02-first-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--編譯器依賴-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
        <!--自定義版本號-->
        <spring-version>4.3.16.RELEASE</spring-version>
    </properties>

    <dependencies>
        <!--自定義工程依賴-->
        <dependency>
            <groupId>org.cyb</groupId>
            <artifactId>01-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--dubbo依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.6.7</version>
        </dependency>
        <!--netty-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.32.Final</version>
        </dependency>
        <!--spring依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring-version}</version>
        </dependency>
    </dependencies>
</project>

spring-dubbo-consumer.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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--當前工程的名稱,監控中心使用-->
    <dubbo:application name="02-first-consumer"/>
    <!--消費引用-->
    <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" url="dubbo://localhost:20880"></dubbo:reference>
</beans>

注:埠號20880固定

RunConsumer.java

package com.cyb.run;

import com.cyb.service.SomeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class RunConsumer {
    public static void main(String[] args) {
        //建立Spring容器
        ApplicationContext ac=new ClassPathXmlApplicationContext("spring-dubbo-consumer.xml");
        SomeService service = (SomeService) ac.getBean("someSerivce");
        service.hello("tom");
    }
}

執行

Zookeeper註冊中心&Zookeeper叢集(Dubbo官網推薦)

拷貝生產者和消費者工程

註冊中心其他方式

地址:http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html

Zookeeper工程搭建

配置參考地址:點我直達

啟動zookeeper服務

修改提供者工程

pom.xml新增curator客戶端依賴 

        <!-- zk客戶端依賴:curator-framework-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>

注意事項

修改spring-dubbo-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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--當前工程名稱,該名稱將在監控平臺使用-->
    <dubbo:application name="03-provider-zk"/>
    <!--註冊Service,將來服務的提供者-->
    <bean id="someService" class="com.cyb.service.SomeServiceImpl"></bean>

    <!-- 宣告zk服務中心 -->
    <!-- 單機版 -->
    <!-- 方式一 -->
    <dubbo:registry address="zookeeper://192.168.1.111:2181"/>
    <!-- 方式二 -->
    <!-- <dubbo:registry protocol="zookeeper" address=""/> -->

    <!-- 叢集配置 -->
    <!-- 方式一 -->
    <!-- <dubbo:registry address="zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181" /> -->
    <!-- 方式二 -->
    <!-- <dubbo:registry protocol="zookeeper" address="10.20.153.10:2181,10.20.153.11:2181,10.20.153.12:2181" /> -->

    <!-- 同一Zookeeper,分成多組註冊中心 -->
    <!-- <dubbo:registry id="chinaRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="china" />
    <dubbo:registry id="intlRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="intl" /> -->

    <!-- 暴露服務,將服務暴露給zk服務中心 -->
<!-- <dubbo:service interface="com.cyb.service.SomeService" ref="someService" registry="N/A"></dubbo:service> -->
    <dubbo:service interface="com.cyb.service.SomeService" ref="someService"></dubbo:service>

</beans>

修改消費者工程

pom.xml新增curator客戶端依賴

        <!-- zk客戶端依賴:curator-framework-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>

修改spring-dubbo-consumer.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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--當前工程的名稱,監控中心使用-->
    <dubbo:application name="03-consumer-zk"/>

    <!-- 宣告zk服務中心 -->
    <!-- 單機版 -->
    <dubbo:registry address="zookeeper://192.168.1.111:2181"/>

    <!--消費引用-->
    <!-- <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" url="dubbo://localhost:20880"></dubbo:reference> -->
    <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService"></dubbo:reference>
</beans>

執行

Dubbo宣告式快取

   為了進一步提高消費者對使用者的響應速度,減少提供者的壓力。Dubbo提供了基於結果的宣告式快取。該快取是基於消費者端的,所以使用很簡單,只需修改消費者配置檔案,與提供者無關。

修改消費者配置檔案

  僅需在<dubbo:reference />中新增cache="true"即可

修改RunConsumer.java

快取VS無快取

  先不加快取,發現提供者執行2次,加上快取提供者只執行一次

dubbo提供了三種結果快取機制

  1. lru:服務級別快取的預設機制。該機制預設可以快取1000個結果。若超出1000,將採用最近最少使用原則來刪除快取,以保證最熱的資料被快取。
  2. threadlocal:當前執行緒快取。當多個執行緒要對當前執行緒進行某一操作時首先需要查詢當前執行緒的某個資訊,通過執行緒快取,則可有效減少查詢。
  3. jcache:可以橋接各種快取實現,即第三方快取產品。

spring-dubbo-consumer.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://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--當前工程的名稱,監控中心使用-->
    <dubbo:application name="03-consumer-zk"/>

    <!-- 宣告zk服務中心 -->
    <!-- 單機版 -->
    <dubbo:registry address="zookeeper://192.168.1.111:2181"/>

    <!-- <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" url="dubbo://localhost:20880"></dubbo:reference> -->
    <!-- <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService"></dubbo:reference> -->

    <!--消費引用:基於服務級別的宣告式快取(結果快取)-->
    <!--<dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" cache="true"></dubbo:reference>-->

    <!--消費引用:基於方法級別的宣告式快取(結果快取)-->
    <dubbo:reference id="someSerivce" interface="com.cyb.service.SomeService" >
        <!--lru-->
        <dubbo:method name="hello" cache="lru"></dubbo:method>
        <!--threadlocal-->
        <dubbo:method name="hello" cache="threadlocal"></dubbo:method>
    </dubbo:reference>
</beans>

結果快取的應用場景

  Dubbo的結果快取可以應用在查詢結果不變的場景。即不能使用在如下場景:消費者A呼叫的業務方法後從DB查詢到一個結果a,此後,消費者B對DB中的該結果相關資料進行了修改,已使該查詢結果變為b,但由於使用了結果快取,消費者A中呼叫業務方法後的查詢結果將長時間為a,直到該結果由於快取空間滿而被消除,否則,永遠無法得到更新過的結果b。

多版本控制

  Dubbo的多版本控制指的是,服務名稱(介面名)相同的情況下提供不同的服務(實現類不同) ,然而消費者是通過服務名稱(介面名)進行服務查詢並進行消費的。提供者所提供的服務名稱相同,如何讓消費者通過名稱進行服務查詢呢?為服務新增一個版本號,使用“服務名稱”+“版本號”的方式來唯一確定一個服務。

  多版本控制主要的應用場景是:當一個介面的實現類需要升級時,可以使用版本號進行過渡(根據開閉原則,不能直接修改原實現類,只能新增新的實現類)。需要注意的是,版本號不同的服務間是不能互相引用的,因為新版本存在的目的是替換老版本。在生產環境中若存在多個提供者需要升級,一般不會一次性全部進行升級,而是會在低壓力時間段先升級一部分,然後在下次再進行部分升級,直到全部升級完成。那麼,這期間就需要使用版本號進行過渡。

專案準備

拷貝專案

提供者(04-provider-version)

 

修改配置檔案

 

消費者(04-consumer-version)

 

執行

服務分組

   服務分組與多版本控制的使用方式幾乎是相同的,只要將version替換為group即可。但使用目的不同。使用版本控制的目的是為了升級,將原有老版本替換掉,將來不再提供老版本的服務,所以不同版本間不能出現相互呼叫。而分組的目的則不同,其也是針對相同介面,給出了多種實現類,但不同的是,這些不同實現並沒有替換掉誰的意思,是針對不同需求,或針對不同功能模組鎖給出的實現。這些實現所提供的服務是並存的,所以他們間可以出現相互呼叫關係。例如,支付服務的實現,可以有微信支付實現與支付寶實現等。

服務暴露延遲

  如果我們的服務啟動過程中需要warmup事件(預熱事件,與JVM重啟後的預熱過程相似,在啟動後一小段事件後效能才能達到最佳狀態)。比如初始化快取,等待相關資源就位等。可以使用deplay進行延遲暴露。

  值需要在服務提供者的<dubbo:service/>標籤中新增delay屬性,其值若為整數,則單位為毫秒,表示在指定事件後再發布服務;若為-1,則表示在spring初始化完畢後再暴露服務。

 

多註冊中心

  很多時候一個專案中會有多個註冊中心。

同一個服務註冊到多箇中心

  同一個服務可以註冊到不同地域的多個註冊中心,以便為不同地域的服務消費者提供更為快捷的服務。

修改服務提供者配置檔案。多個註冊中心之間使用逗號分隔。

 

注:不是叢集!!!

不同服務註冊到不同中心

修改服務提供者配置檔案

同一個服務引用自不同的中心

  同一個消費者需要呼叫兩個不同中心服務,而呼叫的該服務的名稱(介面)、版本等都是相同的。不同中心的這個相同名稱的服務呼叫時不同資料庫中的資料,即相同服務最終執行的結果是不同的。

修改服務消費者配置檔案

 

多協議支援

服務暴露協議

  前面的示例中,服務提供者與服務消費者都是通過zookeeper連線協議連線上ZooKeeper註冊中心的。

 

  提供者與消費者均連線上了註冊中心,那麼消費者就理所當然的可以享受提供者提供的服務了麼?

  實際情況並不是這樣的。前述ZooKeeper協議,是消費者/提供者連線註冊中心的連線協議,而非消費者與提供者間的連線協議。

  當消費者連線上註冊中心後,在消費服務之前,首先需要連線上這個服務提供者。雖然消費者通過註冊中心可以獲取到服務提供者,但提供者對於消費者來說卻是透明的,消費者並不知道真正的服務提供者是誰。不過,無論提供者是誰,消費者都必須連線上提供者才可以獲取到真正的服務,而這個連線也是需要專門的連線協議的。這個協議稱為服務暴露協議。

  可是我們之前的程式碼示例中並沒有看到服務暴露協議的相關配置,但仍可正常執行專案,這是為什麼呢?因為採用了預設的暴露協議:Dubbo服務暴露協議。處理Dubbo服務暴露協議外,Dubbo框架還支援另外七種服務暴露協議:Hessian協議、HTTP協議、RMI協議、WebService協議、Thrift協議、Memcached協議、Redis協議;但在實際生產中,使用最多的就是Dubbo服務暴露協議。

  Dubbo服務暴露協議,適合於小資料量大併發的服務呼叫,以及服務消費者主機數遠大於服務提供者主機數的情況。

服務暴露協議用法

  在服務提供者的spring配置檔案中首先需要註冊暴露協議,然後在暴露服務時具體制定所使用的已註冊的暴露協議。

 

注:protocol屬性用於指定當前服務所使用的暴露協議

同一服務支援多種協議

  直接修改服務提供者的配置檔案

 

不同服務使用不同協議

  直接修改服務提供者配置檔案

 

Dubbo的高階設定及使用建議

在提供者上儘量多配置消費者端屬性

  提供者上儘量多配置消費者端的屬性,讓提供者實現著一開始就思考提供者服務特點、服務質量等問題。因為作服務的提供者,比服務使用方更清楚服務效能引數,如呼叫的超時時間、合理的重試次數等。在提供者端配置後,消費者不配置則會使用提供者端的配置值,即提供者配置可以作為消費者的預設值。否則,消費者會使用消費者端的全域性設定,這對提供者是不可控的,並且往往不合理的。

  以下屬性在<dubbo:method/>上則是針對指定方法,配置在<dubbo:service/>上則是針對整個服務。

  • timeout:遠端服務呼叫超時時限。
  • retries:失敗重試次數,預設值是2。
  • loadbalance:負載均衡演算法,預設是隨機的random。還可以有輪詢roundrobin、最不活躍優先leastactive等。
  • actives:消費者最大併發呼叫限制,即當Consumer對一個服務的併發呼叫到上限後,新呼叫會阻塞直到超時。

提供者上配置合理的提供者端屬性

  • threads:用於指定服務執行緒池大小
  • executes:一個服務提供者並行執行請求上限,即當提供者對一個服務的併發呼叫達到上限後,新呼叫會阻塞,此時消費者可能會超時,該屬性配置在<dubbo:method/>上則是針對指定方法的,配置在<dubbo:service/>上則是針對整個服務