Dubbo入門學習筆記

本文爲博主原創,允許轉載,但請聲明原文地址:http://www.coselding.cn/article/2017-01-02/dubbo-study-record.html

Dubbo是什麼

Dubbo是Alibaba開源的分佈式服務框架,它最大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地鬆耦合)。從服務模型的角度來看,Dubbo採用的是一種非常簡單的模型,要麼是提供方提供服務,要麼是消費方消費服務,所以基於這一點可以抽象出服務提供方(Provider)和服務消費方(Consumer)兩個角色。
簡單說呢,Dubbo用起來就和EJB、WebService差不多,調用一個遠程的服務(或者JavaBean)的時候在本地有一個接口,就像調用本地的方法一樣去調用,它底層幫你實現好你的方法參數傳輸和遠程服務運行結果傳回之後的返回,就是RPC的一種封裝啦~
當然,這個只是Dubbo的最基本的功能,它的特點是:
1. 它主要是使用高效的網絡框架和序列化框架,讓分佈式服務之間調用效率更高。
2. 採用註冊中心管理衆多的服務接口地址,當你想調用服務的時候只需要跟註冊中心詢問即可,不用像使用WebService一樣每個服務都得記錄好接口調用方式。
3. 監控中心:實現對服務方和調用方之間運行狀態的監控,還能控制服務的優先級、權限、權重、上下線等,讓整個龐大的分佈式服務系統的維護和治理比較方便。
4. 高可用:有個服務宕機了?註冊中心就會從服務列表去掉該節點。還是調用到了?客戶端會向註冊中心請求另一臺可用的服務節點重新調用。註冊中心宕機?註冊中心也能實現高可用(ZooKeeper)。
5. 負載均衡:採用軟負載均衡算法實現對多個相同服務的節點的請求負載均衡。
6. 等等。。。很多高大上的,看官方文檔吧,我也是文檔中抄過來的~

一些參考站點或博客

* 注意 *

所有東西以官方用戶指南爲準:http://dubbo.io/User+Guide-zh.htm
其他第三方文章博客什麼的可以參考輔助理解,但是由於阿里是國內公司,官方用戶指南,特別是中文版都特別詳細,真的沒有很多必要去找一堆第三方的教程來看啦~

Dubbo環境準備

Dubbo需要四大基本組件:Registry、Monitor、Provider、Consumer。
image
1. 安裝註冊中心(Registry),我用ZooKeeper,具體參考我的ZooKeeper教程博客,安裝好ZooKeeper之後註冊中心就有了,先放着,等會用。
2. 安裝簡單監控中心:simple-monitor。網上找dubbo-monitor-simple-2.8.4-assembly.tar.gz

  • 解壓,找到conf文件夾下的dubbo.properties文件,下面簡單介紹各個配置參數的意義:
# 容器,就是說這個簡單監控中心是在jetty和spring環境下運行的,依賴於註冊中心,日誌系統是log4j
dubbo.container=log4j,spring,registry,jetty
# 監控系統對整個Dubbo服務系統來說也是一個服務,這裏指定了這個監控服務的名稱
dubbo.application.name=simple-monitor
# 服務的所有者,這是Dubbo的服務的功能,可以指定服務的負責人
dubbo.application.owner=coselding
# 下面四個是指定註冊中心地址的,分別爲廣播、zookeeper、redis、dubbo(自帶)方式的註冊中心,前面說了,我用的是Zookeeper(前面配好了),因此去掉zookeeper前面的#,配置zookeeper的ip和端口號。之後簡單監控中心就能通過註冊中心獲取當前可用的服務列表及其狀態,在頁面向你彙報Dubbo中的服務運行情況。
#dubbo.registry.address=multicast://224.5.6.7:1234
dubbo.registry.address=zookeeper://{ip}:{port}
#dubbo.registry.address=redis://127.0.0.1:6379
#dubbo.registry.address=dubbo://127.0.0.1:9090
# dubbo協議端口號,保持默認即可
dubbo.protocol.port=7070
# jetty工作端口號,平時不是習慣8080嗎?當然,這裏爲了不影響之後運行的tomcat,就不要佔用8080端口啦。
dubbo.jetty.port=8082
# 一個工作目錄,在這個目錄會保存一些監控中心的數據,比如調用曲線圖等,這裏指定一個存在的空目錄即可
dubbo.jetty.directory=${user.home}/monitor
# 監控中心報表存放的目錄,同上,一般默認即可
dubbo.charts.directory=${dubbo.jetty.directory}/charts
# 監控中心數據資料目錄,同上,一般默認即可
dubbo.statistics.directory=${user.home}/monitor/statistics
# 監控中心日誌文件路徑
dubbo.log4j.file=logs/dubbo-monitor-simple.log
# 監控中心日誌記錄級別
dubbo.log4j.level=WARN
  • 運行bin目錄下的start.sh(unix系下)或start.bat(win下)即可。
  • 瀏覽器訪問:http://{簡單監控中心所在的主機ip}:8082,端口號是剛纔配置文件設置的8082,即可訪問查看Dubbo服務集羣中的應用和服務的簡單情況。
  1. 安裝Dubbo管理控制檯
    • 下載dubbo-admin-2.8.4.war,這個是Dubbo的管理控制檯的webapp的war包,將其解壓。
    • WEB-INF目錄下的dubbo.properties文件配置Dubbo的信息,如下:

# 配置註冊中心地址,和簡單監控中心一樣,通過註冊中心才能監控當前所有可用的服務。
dubbo.registry.address=zookeeper://127.0.0.1:2181
# root賬戶的密碼,網頁進入控制檯界面之前需要輸入帳號密碼
dubbo.admin.root.password=root
# guest訪客賬戶的密碼
dubbo.admin.guest.password=guest
  • 將修改完配置的dubbo-admin的整個目錄複製到tomcat的webapps目錄下,重啓tomcat,說白了dubbo-admin就是tomcat的一個webapp的形式存在。

Dubbo註冊中心

上面已經安裝完成了zookeeper的註冊中心了,這個註冊中心主要就是負責dubbo的所有服務地址列表維護,並且可以通過在ZooKeeper節點中設置相應的值來實現對這個服務的權重、優先級、是否可用、路由、權限等的控制。 你可以先記住,之後在Dubbo的管理控制檯對服務的一堆治理策略設置和調整,實際上就是修改了註冊中心中的服務對應的配置數據(即修改了zookeeper中服務對應的節點的配置數據)。 之後`Consumer`從註冊中心請求到服務的數據時就能根據這些配置數據進行相應的治理配置參數的代碼執行生效。

Dubbo樣例服務開發

這裏我用maven構建項目,在Spring環境中配置Provider和Consumer。 先說明使用的依賴:
        <!-- Spring所需依賴 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- dubbo所需依賴 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.8.4</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.0.GA</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.netty</groupId>
            <artifactId>netty</artifactId>
            <version>LATEST</version>
        </dependency>

        <!-- ZooKeeper所需依賴 -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
  1. Provider
  • 聲明服務的接口:
public interface IMyDemo {
        String sayHello(String name);
}
  • 對接口進行實現(這裏是Provider,需要真的實現,之後在Consumer端調用接口之後實際就是在這裏的實現代碼執行所需邏輯的):
public class MyDemo implements IMyDemo {
        @Override
        public String sayHello(String name) {
            String hello = "hello " + name;
            System.out.println(hello);
            return hello;
        }
}
  • Spring配置相應的Dubbo服務(provider.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!-- 這裏添加了dubbo的命名空間,之後Spring通過dubbo中擴展的配置解析生成對應的dubbo實例放到Spring的IoC容器中 -->
<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中層次級別是先分爲多個應用(可以理解爲一個項目),在每個應用下有多個服務(可以理解爲項目下具體的某個提供服務的服務類) -->
    <!-- 這裏就是指定一個應用的名稱,指定相同的應用名的服務在dubbo中都會被分配在同一個應用分支下 -->
    <dubbo:application name="hello-world-app"  />
    <!-- 之前配置好的ZooKeeper服務器作爲註冊中心,這裏指定好ZooKeeper的地址,此處的Dubbo才能連接上註冊服務器,很好理解 -->
    <dubbo:registry address="zookeeper://119.29.153.56:2181" id="registry" />
    <!-- Dubbo的每個節點之間通信可以支持rmi、Http、Dubbo等一系列的協議,這裏指定默認的dubbo協議,以及此處的服務Provider對外暴露的服務端口號 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 到這裏,這個Provider節點就已經能連上註冊服務器,並使用指定的協議進行通信了 -->
    <!-- 這裏對外暴露剛纔編寫的接口,並指定IoC容器中接口的實現來爲這個接口提供實際的服務 -->
    <dubbo:service interface="com.weidian.dubbo.IMyDemo" ref="myDemo"/>
    <!-- 這裏是Spring原始的JavaBean聲明方式,並放在IoC容器中 -->
    <bean id="myDemo" class="com.weidian.dubbo.MyDemo"/>
</beans>
  • 本地測試一下這個服務是否可用,這裏還沒用到Dubbo,只是先測試一下Spring容器是否有問題:
@org.junit.Test
public void testDubbo() throws InterruptedException {
        ApplicationContext providerContext = new ClassPathXmlApplicationContext("provider.xml");
        IMyDemo demo = providerContext.getBean(IMyDemo.class);
        System.out.println(demo.sayHello("world"));
        Thread.sleep(60000);
    }
  • 運行結果:
    這裏寫圖片描述
    Provider內部代碼輸出了一遍,返回到測試代碼又輸出了一遍,總共兩遍hello world
    1. Consumer
  • 編寫和Provider服務的對應接口:
public interface IMyDemo {
        String sayHello(String name);
}
  • Spring配置遠程的服務爲本地的JavaBean(consumer.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!-- 這裏添加了dubbo的命名空間,之後Spring通過dubbo中擴展的配置解析生成對應的dubbo實例放到Spring的IoC容器中 -->
<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">
    <!-- 聲明要連接的應用名稱,要和Provider聲明的指定的應用名一致 -->
    <dubbo:application name="hello-world-app"  />
    <!-- 之前配置好的ZooKeeper服務器作爲註冊中心,這裏指定好ZooKeeper的地址,此處的Dubbo才能連接上註冊服務器,很好理解 -->
    <dubbo:registry address="zookeeper://119.29.153.56:2181" id="registry" />
    <!-- Dubbo的每個節點之間通信可以支持rmi、Http、Dubbo等一系列的協議,這裏指定默認的dubbo協議,
        以及此處的Consumer對外暴露的服務端口號,因爲註冊中心有服務節點列表更新是要實時推送到Consumer中的 
        -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 到這裏,這個Consumer節點就已經能連上註冊服務器,並使用指定的協議進行通信了 -->
    <!-- 接口指定Consumer端的那個服務接口,之後它就會通過這個接口的應用名和全限定名去註冊中心
        查找實際的服務Provider地址列表,再通過指定的dubbo協議進行通信,實現RPC,而在Consumer
        本機端對Spring的IoC容器指定id,方便之後對這個遠程JavaBean的引用調用 -->
    <dubbo:reference id="demoRemote" interface="com.weidian.dubbo.IMyDemo" protocol="dubbo"/>
</beans>
  • 另外,也能繞過Registry直連Provider,如下:
<dubbo:reference interface="com.weidian.dubbo.IMyDemo" version="1.0" id="myDemo" url="dubbo://127.0.0.1:20880/"></dubbo:reference>
  • 遠程過程調用測試(前提是先把Provider的服務先運行起來再來運行這個Consumer(在兩個端的測試代碼尾部都添加了sleep代碼的原因):
@org.junit.Test
public void testGetRemoteService() throws InterruptedException {
        ApplicationContext consumerContext = new ClassPathXmlApplicationContext("consumer.xml");
        IMyDemo demoRemote = consumerContext.getBean(IMyDemo.class);
        System.out.println(demoRemote.sayHello("world"));
        Thread.sleep(30000);
    }
  • 運行結果:
    Provider:
    image
    啓動Provider輸出了兩遍,Consumer調用時本地實現又輸出了一遍,共三遍。
    Consumer:
    這裏寫圖片描述
    啓動Consumer,遠程返回輸出了一遍。

Dubbo簡單監控中心

簡單介紹,其實用處不大,我覺得管理後臺的功能已經把這個監控中心的功能覆蓋了,僅僅只是拿來測試使用一下。

  • 主界面:
    這裏寫圖片描述
  • 服務列表界面,顯示所有服務以及它的Provider和Consumer情況:
    這裏寫圖片描述

Dubbo管理後臺使用

之前Tomcat中配置好了dubbo-admin的webapp,現在只要打開tomcat,並輸入相應的地址即可訪問dubbo-admin的界面,如我的是http://127.0.0.1:8080/dubbo-admin/
其實就是一個很常見的管理後臺,可以控制每個服務、應用的狀態、權重、路由控制、訪問控制、負載均衡、各個應用的服務情況和消費情況等,不需要教程,直接上手使用即可,至於其中的一些可能有疑問的概念,下面的內容將一一說明。

服務路由

路由,顧名思義,就是通過配置去設定哪些Consumer節點的請求由哪些節點的Provider節點的服務來進行響應,可以在一定程度上控制負載分佈。
知道這個概念,那剩下就是配置的問題了,明白怎麼回事其實就很簡單啦~

在dubbo-admin主界面——服務治理——路由規則,如下:
這裏寫圖片描述
點擊新增
這裏寫圖片描述
路由名稱:爲你定義的這個路由規則聲明一個名稱,之後可以根據這個名稱來找這個路由規則。
優先級:很明顯,一個int數值代表這個路由規則的優先級,優先級越高這個規則越先匹配。
服務名:列表選擇的,從註冊中心中已有的服務列表中選擇一個服務,表示這個路由規則要約束的是哪個服務。
方法名:列表選擇的,選擇要約束的這個服務中的哪個方法。
匹配條件:填寫匹配下面的列表就表示匹配了條件才受這個路由規則約束,否則就是不匹配規則才受該規則約束。
消費者IP地址:要約束的Consumer的IP地址列表,逗號隔開。
消費者應用名:要約束的Consumer的應用名列表。
消費者集羣:按照提示上寫的去找,沒找到相應的選項,暫時也沒用到,之後再回頭編輯。
過濾規則:填寫匹配下面的列表就表示匹配了條件才受這個路由規則約束,否則就是不匹配規則才受該規則約束。
提供者IP地址:要約束的Provider的IP地址列表,逗號隔開。
提供者集羣:按照提示上寫的去找,沒找到相應的選項,暫時也沒用到,之後再回頭編輯。
提供者協議:指定Provider的協議,不是該協議的也不約束。
提供者端口:指定Provider的端口,不是這個端口的也不約束。

測試樣例展示:
這裏寫圖片描述
這樣配置的路由就能夠生效,效果是:com.weidian.dubbo.IMyDemo服務的sayHello方法下,地址爲192.168.31.164的Consumer的請求由(地址:192.168.31.164;協議:dubbo;端口:20880)的Provider進行響應,如果沒有匹配的Provider就會默認返回所有的Provider列表給Consumer,讓Consumer自己選。
點擊保存,保存新增的路由規則,並在路由規則列表中啓用這個規則,如下:
這裏寫圖片描述
點擊預覽,在消費者地址填寫192.168.31.164,點擊預覽,如下圖所示:
這裏寫圖片描述
運行Consumer程序,查看消費者狀態,可知設定的這個路由規則對該對應地址的消費真生效了,如下圖:
這裏寫圖片描述
另外,IP地址支持結尾爲匹配所有,如10.0.0.或者10.0.*等。
不匹配的配置規則和匹配的配置規則是一致的。

負載均衡

dubbo提供4種負載均衡方式:
Random:隨機,按權重配置隨機概率,調用量越大分佈越均勻,默認是這種方式
RoundRobin:輪詢,按權重設置輪詢比例,如果存在比較慢的機器容易在這臺機器的請求阻塞較多
LeastActive:最少活躍調用數,不支持權重,只能根據自動識別的活躍數分配,不能靈活調配
ConsistentHash:一致性hash,對相同參數的請求路由到一個服務提供者上,如果有類似灰度發佈需求可採用
dubbo的負載均衡機制是在客戶端調用時通過內存中的服務方信息及配置的負責均衡策略選擇,如果對自己系統沒有一個全面認知,建議先採用random方式。

Dubbo過濾器

有需要自己實現dubbo過濾器的,可關注如下步驟:
1. dubbo初始化過程加載ClassPath下的META-INF/dubbo/internal/META-INF/dubbo/META-INF/services/三個路徑(classloader resource)下面的com.alibaba.dubbo.rpc.Filter文件。
文件內容:
每行Name=FullClassName,這些類必須是實現Filter接口,如下圖:
這裏寫圖片描述
2. 自定義Filter類:

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
//激活這個過濾器的註解,標記這個過濾器在消費者端加入過濾器鏈
@Activate(group = Constants.CONSUMER)
//dubbo過濾器的實現類
public class DubboTestFilter implements Filter {
    /**計時器過濾器,記錄這個RPC的整個過程執行時間
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long start = System.currentTimeMillis();
        //這個是RPC的實現體,Result就是RPC的執行結果,和Servlet的過濾器有些類似
        Result result = invoker.invoke(invocation);
        System.out.println("time = " + (System.currentTimeMillis() - start) + "ms");
        System.out.println(result.getValue());
        return result;
    }
}
  1. consumer.xml配置文件中配置這個過濾器,如下圖:
    這裏寫圖片描述
  2. 先來看看配置完成之後的執行效果:
    這裏寫圖片描述

    對比上面的過濾器代碼可知,先輸出了RPC過程的運行時間,之後在過濾器中輸出了一遍執行結果hello world,之後單元測試本身再把執行結果輸出一遍,因此hello world輸出了兩遍。

  3. Dubbo過於這個過濾器的加載過程:

    (1) 先加載那三個路徑下的com.alibaba.dubbo.rpc.Filter文件裏面的鍵值對,key爲過濾器的名稱,value爲過濾器的類全限定名(這個類必須實現Dubbo中的Filter接口);
    (2) 這樣就能找到這個類的class文件了,檢查@Activate註解加載這個過濾器設定的一些全局基本屬性;
    (3) Spring在加載consumer.xml文件的時候,通過

<dubbo:consumer filter="dubboTestFilter" id="dubboTestFilter" retries="0"/>

指定消費者端要加載的過濾器,通過filter屬性指定過濾器名稱(就是配置文件中的過濾器key),這樣剛纔加載的過濾器類就加入消費者代碼邏輯中的過濾器鏈了。
6. 關於@Activate註解: —— 自動激活

group:(provider|consumer)匹配了對應的角色才被加載
value:標明過濾條件,不寫則所有條件下都會被加載,寫了則只有dubbo URL中包含該參數名且參數值不爲空才被加載,這個參數會以dubbo協議的一個參數K-V對傳到Provider。


還欠缺安全保護和集羣性能調優方面的講解,畢竟纔剛入門,待我在公司的項目中使用一段時間有了真正的一些實戰感悟再來好好總結~

本文爲博主原創,允許轉載,但請聲明原文地址:http://www.coselding.cn/article/2017-01-02/dubbo-study-record.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章