天天看點

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

Dubbo學習筆記

  • Dubbo概述
    • 分布式技術圖譜
    • 系統架構的發展
      • 單體系統架構
      • 叢集架構
      • 分布式架構
      • 微服務架構
      • 流動計算架構
    • 小插曲-架構師的基本素養
      • 常用術語
        • 系統容量與系統容量預估
        • QPS
        • PV
        • UV
      • 系統容量預估計算
        • 帶寬計算
        • 并發量計算
        • 伺服器預估量
    • Dubbo簡介
      • 官網簡介
      • 什麼是RPC?
      • Dubbo重要時間點
  • Dubbo入門篇
    • Dubbo示範項目架構搭建
    • 第一個Dubbo程式(直連式)
      • 建立業務接口子產品 00-api
      • 建立提供者子產品 01-provider
      • 建立消費子產品 01-consumer
    • Zookeeper注冊中心
      • 建立提供者02-provider-zk
      • 建立消費者02-consumer-zk
      • 添加日志檔案
    • 将Dubbo應用到web工程
      • 建立提供者 03-provider-web
      • 建立消費者 03-consumer-web
      • 部署運作
    • Dubbo管理控制台
      • 下載下傳
      • 配置
      • 打包
      • 運作
    • 關閉服務檢查
      • 問題複現
      • 分析
    • 多版本控制
      • 建立提供者 04-provider-version
      • 建立消費者 04-consumer-version
    • 服務分組
      • 建立提供者 05-provider-group
      • 建立消費者 05-consumer-group
    • 多協定支援
      • 各個協定的特點
        • dubbo協定
        • rmi協定
        • hession協定
        • http協定
        • webService協定
        • thrift協定
        • memcached協定與redis協定
        • rest協定
      • 用法
        • 同一服務支援多種協定
        • 不同服務使用不同協定

Dubbo概述

分布式技術圖譜

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

系統架構的發展

單體系統架構

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 當站點功能與流量都很小時,隻需一個應用,将所有功能都集中在一個工程中,并部署在一台機器上,以減小部署節點和成本。例如,将使用者子產品、訂單子產品、支付子產品等都做在一個工程中,以一個應用的形式部署在一台伺服器上。

叢集架構

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 當站點流量增加而單體架構無法應用其通路量時,可通過搭建叢集增加主機的方式提升系統的性能。這種方式為水準擴充。

分布式架構

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 當通路量逐漸增大,叢集架構的水準擴充,其所帶來的效率提升越來越小。此時可以将項目拆分成多個功能相對獨立的子工程提升效率。例如使用者工程、訂單工程、支付工程等。這種稱為垂直擴充。

微服務架構

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 當子工程越來越多時,發現它們可能同時都擁有某功能相同或相似的子產品,于是将這些在整個項目中備援的功能子產品抽取出來作為單獨的工程,這些工程就是專門為那些調用它們的工程服務的。那麼這些抽取出來的功能稱為微服務,微服務應用稱為服務提供者,而調用微服務的應用就稱為服務消費者。

流動計算架構

JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 随着功能的擴張,微服務就需要越來越多;随着 PV 的增漲,消費者工程就需要越來越多;随着消費者的擴張,為其提供服務的提供者伺服器就需要越來越多,且每種提供者都要求建立為叢集。這樣的話,消費者對于提供者的通路就不能再采用直連方式進行了,此時就需要服務注冊中心了。提供者将服務注冊到注冊中心,而消費者通過注冊中心進行消費,消費者無需再與提供者綁定了。提供者的當機,對消費者不會産生直接的影響。
  • 随着業務的增多,在一些特殊時段(例如雙11)就會出現服務資源浪費的問題:有些服務的 QPS(Query Per Second) 很低,但其還占用着很多系統資源,而有些 QPS 很高,已經出現了資源緊張,使用者體驗驟降的情況。此時就需要服務治理中心了。讓一些不重要的服務暫時性降級,或為其配置設定較低的權重等,對整個系統資源進行統一調配。
  • 這裡的資源調配分為兩種:預調配與實時調調配。
    • 預調配:根據系統架構師的“系統容量預估”所進行的調配,是一種經驗,是一種預處理,就像每年雙11期間的 PV(Page View) 與 UV(Unique Visitor) 都會很高,就需要提前對各服務性能進行調配。
    • 實時調配:根據服務監控中心所提供的基于通路壓力的實時系統容量評估資料,對各服務性能進行實時調配的方案。

小插曲-架構師的基本素養

常用術語

系統容量與系統容量預估

  • 系統容量指系統所能承受的最大通路量,而系統容量預估則是在峰值流量到達之前系統架構師所給出的若幹技術名額值。常用的技術名額值有:QPS、PV、UV、并發量、帶寬、CPU使用率、記憶體硬碟占用率等。系統容量預估是架構師必備的技能之一。

QPS

  • QPS:Query Per Second,每秒查詢量。在分布式系統中 QPS 的定義是:單個程序每秒請求伺服器的成功次數。QPS一般可以通過壓力測試工具測得,例如LoadRunner、Apache JMeter、NeoLoad、http_load等。
  • QPS = 總請求數 / 程序總數 / 請求時間 = 總請求數 / (程序總數*請求時間)

PV

  • PV:Page View,頁面通路量。指一定時間範圍内,打開或重新整理頁面的次數,一般以 24 小時計算。

UV

  • UV:Unique Visitor,獨立訪客數量。指一定時間範圍内,站點通路所來自的 IP 數量。同一IP多次通路站點隻計算一次,一般以 24 小時計算。

系統容量預估計算

帶寬計算

  • 平均帶寬的計算公式:
平均帶寬 = 總流量數(bit)/産生這些流量的時長(秒)= (PV * 頁面平均大小 * 8) / 統計時間(秒)
說明:公式中的 8 指的是将Byte轉換為bit,即8b/B,因為帶寬的機關是bps(比特率),即bit per second,每秒二進制位數,而容量機關一般使用Byte。
  • 假設某站點的日均 PV 是 10w,頁面的平均大小為 0.4M,那麼其平均帶寬需求是:平均帶寬 = (10w * 0.4M * 8) / (60*60*24) = 3.7Mbps
  • 以上計算的僅僅是平均帶寬,我們在進行容量預估時需要的時峰值帶寬,即必須要保證站點在峰值時能夠正常運轉。假設,峰值流量是平均流量的 5 倍,這個 5 倍稱為峰值因子。安照這個計算,實際需要的帶寬大約在 3.7Mbps*5 = 18.5Mbps。
  • 峰值帶寬 = 平均帶寬 * 峰值因子

并發量計算

  • 并發量,也稱為并發連接配接數,一般是指單台伺服器每秒處理的連接配接數。平均并發連接配接數的計算公式是:
平均并發連接配接數 = (站點PV * 頁面平均衍生連接配接數) / (統計時間 * web伺服器數量)
說明:頁面平均衍生連接配接數指的是,一個頁面請求所産生的 http 連接配接數量,如對靜态資源的 css、js、images等的請求數量。這個值需要根據實際情況而定。
  • 例如,一個由 5 台 web 主機構成的叢集,其日均 PV 50w,每個頁面平均 30 個衍生連接配接,則其平均并發連接配接數為:平均并發量 = (50w * 30) / (60*60*24*5) = 35;若峰值因子為6,則峰值并發量為:峰值并發量 = 平均并發量 * 峰值因子 = 35 * 6 = 210。

伺服器預估量

  • 根據往年同期獲得的日均 PV、并發量、頁面衍生連接配接數,及公司業務擴充所帶來的流量增漲率,就可以計算出伺服器預估值。
  • 注意,今年的頁面衍生連接配接數與往年的可能不一樣。
伺服器預估值 = 站點每秒處理的總連接配接數 / 單機并發連接配接數 = (PV * 頁面衍生連接配接數 * (1 + 增漲率)) / 統計時間 / 單機并發連接配接數
注意:統計時間,即PV的統計時間,一般為一天。

Dubbo簡介

官網簡介

  • Dubbo官網為:http://dubbo.apache.org。該官網是 Dubbo 正式進入 Apache 開源孵化器後改的。Dubbo原官網為:http://dubbo.io。
  • Dubbo官網已做過了中英文國際化,使用者可以在中英文間任意切換。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

什麼是RPC?

  • RPC(Remote Procedure Call Protocol)——遠端過程調用協定,是一種通過網絡從遠端計算機程式上請求服務,而不需要了解底層網絡技術的協定。RPC協定假定某些傳輸協定的存在,如TCP或UDP,為通信程式之間攜帶資訊資料。在OSI網絡通信模型(OSI七層網絡模型,OSI:Open System Interconnection,開放系統互聯)中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分布式多程式在内的應用程式更加容易。
  • RPC采用客戶機/伺服器模式(即C/S模式)。請求程式就是一個客戶機,而服務提供程式就是一個伺服器。首先,客戶機調用程序發送一個有程序參數的調用資訊到服務程序,然後等待應答資訊。在服務端,程序保持睡眠狀态直到調用資訊到達為止。當一個調用資訊到達,伺服器獲得程序參數,計算結果,發送答複資訊,然後等待下一個調用資訊,最後,用戶端調用程序接收答複資訊,獲得程序結果,然後調用執行繼續進行。

Dubbo重要時間點

Dubbo發展過程中的重要時間點:

  • 2011年開源,之後就迅速成為了國内該開源項目的佼佼者。2011年時,優秀的、可在生成環境使用的RPC架構很少,Dubbo的出現迅速給人眼前一亮的感覺,迅速受到了開發者的親睐。
  • 2012年10月之後,就基本停止了重要更新,改為階段性維護。
  • 2014年10月30日釋出2.4.11版本後,Dubbo停止更新。
  • 2017年10月雲栖大會上,阿裡宣布Dubbo被列入集團重點維護開源項目,這也就意味着Dubbo起死回生,開始重新進入快車道。
  • 2018年2月15日,大年三十,進過一系列緊張的投票,宣布 Dubbo 正式進入 Apache 孵化器。

Dubbo入門篇

Dubbo示範項目架構搭建

  • 源碼 github 位址:https://github.com/shouwangyw/dubbo
  • 項目工程名:dubbo-example-parent
  • Dubbo版本号與zk用戶端:Dubbo 在 2.6.0 及其以前版本時,預設使用的用戶端為 ZkClient。2.6.1 版本,将預設用戶端由 ZkClient 修改為 Curator。至于 Curator 的版本,與 Dubbo 及所要連接配接的 Zookeeper 的版本有關。目前其支援的版本為 2.x.x 版本,最高版本為 2.13.0。
  • Dubbo與Spring的版本号:Dubbo 的使用是基于 Spring 環境下的,即 Dubbo 是依賴于 Spring 架構的。Dubbo2.7.0依賴的 Spring 是4.3.16,是以在Dubbo的開發過程中最好使用與該Spring版本相同的 Spring,這樣可以避免可能的版本沖突問題。
<!-- dubbo依賴 -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.7.0</version>
</dependency>
           

第一個Dubbo程式(直連式)

建立業務接口子產品 00-api

  • 業務接口名即服務名稱。無論是服務提供者向服務注冊中心注冊服務,還是服務消費者從注冊中心索取服務,都是通過接口名稱進行注冊與查找的。即,提供者與消費者都依賴于業務接口。是以,一般情況下,會将業務接口專門定義為一個工程,讓提供者與消費者依賴。
/**
 * 業務接口
 */
public interface SomeService {
    String hello(String name);
}
           

建立提供者子產品 01-provider

  • 建立 01-provider 的 Maven 工程,在 pom.xml 中引入業務接口依賴
<dependency>
  <groupId>com.yw.dubbo.example</groupId>
  <artifactId>00-api</artifactId>
  <version>1.0</version>
</dependency>
           
  • 業務接口實作類:
public class SomeServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println(name + ",我是提供者");
        return "Hello Dubbo World! " + name;
    }
}
           
  • spring-provider.xml 配置檔案
└── resources
    ├── META-INF
    │   └── spring
    │       └── spring-provider.xml  // 啟動方式二: Dubbo Main方式啟動
    └── spring-provider.xml // 啟動方式一: Spring 容器啟動 

<?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://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
  <!-- 指定目前工程在Monitor中顯示的名稱,一般與工程名相同 -->
  <dubbo:application name="01-provider" />
  <!-- 指定服務注冊中心:不指定注冊中心 -->
  <dubbo:registry address="N/A" />
  <!-- 注冊服務執行對象 -->
  <bean id="someService" class="com.yw.dubbo.example.provider.SomeServiceImpl" />
  <!-- 服務暴露 -->
  <dubbo:service interface="com.yw.dubbo.example.service.SomeService" ref="someService" />
</beans>
           
  • 提供者啟動類:
public class ProviderRun {
    // 啟動方式一:
//    public static void main(String[] args) throws Exception {
//        // 建立Spring容器
//        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-provider.xml");
//        // 啟動Spring容器
//        ac.start();
//        // 使主線程阻塞
//        System.in.read();
//    }
    // 啟動方式二:要求Spring配置檔案必須要放到類路徑下的 META-INF/spring 目錄中
    public static void main(String[] args) {
        Main.main(args);
    }
}
           

建立消費子產品 01-consumer

  • spring-consumer.xml 配置檔案:
<!-- 指定目前工程在Monitor中顯示的名稱,一般與工程名相同 -->
<dubbo:application name="01-consumer"/>
<!-- 指定服務注冊中心:不指定注冊中心 -->
<dubbo:registry address="N/A"/>
<!-- 訂閱服務:采用直連式連接配接消費者 -->
<dubbo:reference id="someService" interface="com.yw.dubbo.example.service.SomeService" url="dubbo://localhost:20880"/>
           
  • 消費者啟動類:
public class ConsumerRun {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-consumer.xml");
        SomeService service = (SomeService) ac.getBean("someService");
        String hello = service.hello("China");
        System.out.println(hello);
    }
}
           
  • 分别啟動運作 01-provider 和 01-consumer,檢視結果

Zookeeper注冊中心

  • 在生産環境下使用最多的注冊中心為Zookeeper,當然Redis也可以做注冊中心。下面就來學習Zookeeper作為注冊中心的用法。

建立提供者02-provider-zk

  • 建立工程:複制前面的提供者工程01-provider,并更名為02-provider-zk。
  • 修改父工程 pom.xml檔案,并在其中添加 Zookeeper 用戶端依賴 curator。
<!-- zk用戶端依賴:curator -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.13.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.13.0</version>
</dependency>
           
  • 修改 spring 配置檔案:指定服務注冊中心位址
<!-- 指定服務注冊中心:zk單機 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181" />
<!--  <dubbo:registry protocol="zookeeper" address="192.168.254.120:2181" />  -->

<!-- 指定服務注冊中心:zk叢集 -->
<!--  <dubbo:registry address="zookeeper://192.168.254.128:2181?backup=192.168.254.130:2181,192.168.254.132:2181,192.168.254.129:2181"/> -->
<!--  <dubbo:registry protocol="zookeeper" address="192.168.254.128:2181,192.168.254.130:2181,192.168.254.132:2181,192.168.254.129:2181"/> -->
           

建立消費者02-consumer-zk

  • 建立工程:複制前面的提供者工程01-consumer,并更名為02-consumer-zk。
  • 修改 spring 配置檔案:
<!-- 指定服務注冊中心:不指定注冊中心 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181"/>
<!-- 訂閱服務 -->
<dubbo:reference id="someService" check="false" 
                 interface="com.yw.dubbo.example.service.SomeService" />
           

添加日志檔案

  • 通過前面的運作可知,無論是提供者還是消費者,控制台給出的提示資訊都太少,若想看到更多的資訊,可以在提供者與消費者工程的類路徑 src/main/resources 下添加日志檔案。可以添加 log4j.xml,即使用 log4j2 日志技術;也可以添加 log4j.properties,即使用 log4j 日志技術。我們這裡添加 log4j.properties 檔案。
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n

log4j.rootLogger=info,console
           
  • 提供者添加日志檔案:運作後可以看到如下日志輸出。其中最為重要的的是 provider://xxx,這裡顯示的就是目前工程所提供的能夠被訂閱的服務描述,即服務中繼資料資訊。另外,我們還可以看到目前應用于與 **qos-server(Quality of Service伺服器,即Dubbo的管控平台)**進行通信的端口号為 22222。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 消費者添加日志檔案:運作後在控制台的日志輸出中可以看到報錯。其報錯内容是,消費者連接配接 qos-server 的端口号被占用了。其與 qos-server 通信的端口号預設也為 22222,已經被提供者給占用了。當然,原因主要是由于消費者與提供者都在同一主機,若分别存在于不同的主機也就不會報錯了。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 是以解決方案就是為消費者修改與 qos-server 通信的端口号。有兩種修改方式,方式一是在 src/main/resources 中建立一個 dubbo.properties 檔案,檔案内容僅需加入一行:
dubbo.application.qos.port=3333
           
  • 方式二就是直接修改 spring-consumer.xml 檔案:
<dubbo:application name="02-consumer-zk">
	<!-- 如果 dubbo.properties 中配置了,則不會使用這裡配置的 -->
    <dubbo:parameter key="qos.port" value="4444"/>
</dubbo:application>
           

将Dubbo應用到web工程

  • 前面所有提供者與消費者均是 Java 工程,而在生産環境中,它們都應是 web 工程,Dubbo 如何應用于 Web 工程呢?

建立提供者 03-provider-web

  • 建立工程:複制 02-provider-zk 工程,然後在此基礎上修改,packaging方式設定為 war。
  • 導入依賴:在父工程添加 springmvc、servlet與jsp依賴
<!-- SpringMVC相關依賴 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>${spring-version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${spring-version}</version>
</dependency>
<!-- Servlet依賴 -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
</dependency>
<!-- JSP依賴 -->
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>javax.servlet.jsp-api</artifactId>
  <version>2.2.1</version>
</dependency>
           
  • 定義web.xml:webapp/WEB-INF 目錄下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  <!-- 注冊Spring配置檔案 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-*.xml</param-value>
  </context-param>

  <!-- 注冊ServletContext監聽器 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>
           

建立消費者 03-consumer-web

  • 建立工程:複制02-consumer-zk 工程,然後在此基礎上修改,依賴與提供者工程中的依賴相同
  • 定義處理器:
@Controller
public class SomeController {
    @Autowired
    private SomeService service;

    @RequestMapping("/some.do")
    public String someHandle(){
        String result = service.hello("China");
        System.out.println("消費者端接收到 = " +  result);
        return "/welcome.jsp";
    }
}
           
  • welcome.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>Welcome you!</h2>
</body>
</html>
           
  • 定義web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  <!-- 對于2.6.4版本,其Spring配置檔案必須指定從<context-param>中加載 -->
  <!--<context-param>-->
  <!--<param-name>contextConfigLocation</param-name>-->
  <!--<param-value>classpath:spring-*.xml</param-value>-->
  <!--</context-param>-->

  <!-- 字元編碼過濾器 -->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- 注冊中央排程器 -->
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- 不能寫/*,不建議寫/,建議擴充名方式 -->
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>
           
  • 修改spring-consumer.xml:注冊處理器
<beans ...略
  <!-- 注冊處理器 -->
  <mvc:component-scan base-package="com.yw.dubbo.example" />
</beans>
           

部署運作

  • 修改Tomcat預設端口号:由于Dubbo管控台端口為8080,是以這裡将Tomcat預設端口号修改為8088。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 啟動運作Tomcat進行測試:接口 http://localhost:8088/consumer/some.do
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

Dubbo管理控制台

  • 2019年初,官方釋出了 Dubbo 管控台 0.1 版本。結構上采取了前後端分離的方式,前端使用 Vue 和 Vuetify 分别作為Javascript架構和UI架構,後端采用Spring Boot架構。

下載下傳

  • Dubbo管理控制台的下載下傳位址為:https://github.com/apache/incubator-dubbo-ops

配置

  • 在下載下傳的zip檔案的解壓目錄的 dubbo-admin-server\src\main\resources 下,修改配置檔案 application.properties,主要就是修改注冊中心、配置中心與中繼資料中的 zk 位址。
# centers in dubbo2.7
admin.registry.address=zookeeper://192.168.254.120:2181
admin.config-center=zookeeper://192.168.254.120:2181
admin.metadata-report.address=zookeeper://192.168.254.120:2181
           
  • 這是一個springboot工程,預設端口号為8080,若要修改端口号,則在配置檔案中增加形如 server.port=8888 的配置。

打包

  • 在指令行視窗中進入到解壓目錄根目錄,執行打包指令:mvn clean package,當看到以下提示時表示打包成功:
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 打包結束後,進入到解壓目錄的 dubbo-admin-distribution 目錄下的target目錄。目錄下有個 dubbo-admin-0.2.0.jar 檔案。該jar封包件即為 Dubbo 管理控制台的運作檔案,可以将其放到任意目錄下運作。

運作

  • 先啟動 zk,再啟動管控台:
java -jar dubbo-admin-0.2.0.jar -d
           
  • 通路:在浏覽器位址欄中輸入 http://localhost:8080,即可看到 Dubbo 管理控制台界面。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

關閉服務檢查

問題複現

  • 修改工程 02-consumer-zk的啟動類ConsumerRun,将對消費者調用提供者的服務方法注釋掉,使消費者不調用該方法。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 運作測試:會發生報錯,提示服務狀态不可用,沒有可用的提供者。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 錯誤原因是檢查 SomeService 的狀态失敗,可以通過如下修改防止報錯:再運作消費者工程就不會報錯了。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

分析

  • 預設情況下,若服務消費者先于服務提供者啟動,則消費者會報錯。因為預設情況下,消費者會在啟動時檢查其要消費的服務提供者是否已經注冊,若未注冊則抛出異常。可以在消費者端的spring配置檔案中添加 **check=“fasle”**屬性,關閉服務檢查功能。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 隻要注意啟動順序,該屬性看似可以不使用。但在循環消費場景下是必須要使用的。即A消費B服務,B消費C服務,而C消費A服務。這是典型的循環消費。在該場景下必須至少有一方關閉服務檢查功能,否則将無法啟動任何一方。

多版本控制

  • 對于整個系統的更新,為了保證系統更新的穩定性與安全性,一般并不會讓所有消費者一下全部都改為調用新的實作類,而是有個“灰階釋出(又稱為金絲雀釋出)”過程,即有個新老交替的過程。即在低壓力時段,讓一部分消費者先調用新的提供者實作類,其餘的仍然調用老的實作類,在新的實作類運作沒有問題的情況下,逐漸讓所有消費者全部調用成新的實作類。而多版本控制就是實作灰階釋出的。

建立提供者 04-provider-version

  • 建立工程:複制前面的提供者工程 02-provider-zk,并更名為 04-provider-version。
  • 定義兩個接口實作類:删除原來的 SomeServiceImpl類,并建立兩個實作類:
public class OldSomeServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("執行【老】的提供者OldSomeServiceImpl的hello()");
        return "OldSomeServiceImpl";
    }
}
public class NewSomeServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("執行【新】的提供者NewSomeServiceImpl的hello()");
        return "NewSomeServiceImpl";
    }
}
           
  • 修改配置檔案:指定版本 0.0.1 對應的是 oldService 執行個體,而版本 0.0.2 對應的是 newService 執行個體。
<!-- 指定目前工程在Monitor中顯示的名稱,一般與工程名相同 -->
<dubbo:application name="04-provider-version" />

<!-- 指定服務注冊中心:zk單機 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181" />

<!-- 注冊Service實作類 -->
<bean id="oldService" class="com.yw.dubbo.example.provider.OldSomeServiceImpl" />
<bean id="newService" class="com.yw.dubbo.example.provider.NewSomeServiceImpl" />
<!-- 服務暴露 -->
<dubbo:service interface="com.yw.dubbo.example.service.SomeService" ref="oldService" version="0.0.1" />
<dubbo:service interface="com.yw.dubbo.example.service.SomeService" ref="newService" version="0.0.2"/>
           

建立消費者 04-consumer-version

  • 建立工程:複制前面的提供者工程 02-consumer-zk,并更名為 04-consumer-version。
  • 修改配置檔案:
<!--  &lt;!&ndash;  訂閱服務:指定消費0.0.1版本,即oldService提供者 &ndash;&gt;-->
<!--  <dubbo:reference id="someService" check="false" version="0.0.1" protocol="dubbo"-->
<!--                   inter />-->

<!--  訂閱服務:指定消費0.0.2版本,即newService提供者 -->
<dubbo:reference id="someService" version="0.0.2" interface="com.yw.dubbo.example.service.SomeService"/>
           

服務分組

  • 服務分組與多版本控制的使用方式幾乎是相同的,隻要将 version 替換為 group 即可。但使用目的不同,使用版本控制的目的是為了更新,将原有老版本替換掉,将來不再提供老版本的服務,是以不同版本不能出現互相調用。而分組的目的則不同,其也是針對不同需求,給出了多種實作。但是不同的是,這些不同實作并沒有誰替換掉誰的意思,是針對不同的需求,或針對不同功能子產品所給出的不同實作。這些實作所提供的服務是并存的,是以它們間可以出現互相調用關系。例如,對于支付服務的實作,可以有微信支付實作與支付寶實作等。

建立提供者 05-provider-group

  • 建立工程:複制前面的提供者工程 04-provider-version,并更名為 05-provider-group。
  • 定義兩個接口實作類:删除原來的兩個接口實作類,重新定義兩個新的實作類。
public class WeixinServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("使用【微信】支付");
        return "WeixinServiceImpl";
    }
}
public class ZhifubaoServiceImpl implements SomeService {
    @Override
    public String hello(String name) {
        System.out.println("使用【支付寶】支付");
        return "ZhifubaoServiceImpl";
    }
}
           
  • 修改配置檔案:
<!-- 指定目前工程在Monitor中顯示的名稱,一般與工程名相同 -->
<dubbo:application name="05-provider-group"/>
<!-- 指定服務注冊中心:zk單機 -->
<dubbo:registry address="zookeeper://192.168.254.120:2181"/>
<!-- 注冊Service實作類 -->
<bean id="weixinService" class="com.yw.provider.WeixinServiceImpl"/>
<bean id="zhifubaoService" class="com.yw.provider.ZhifubaoServiceImpl"/>
<!-- 服務暴露 -->
<dubbo:service interface="com.yw.service.SomeService" ref="weixinService" group="pay.weixin"/>
<dubbo:service interface="com.yw.service.SomeService" ref="zhifubaoService" group="pay.zhifubao"/>
           

建立消費者 05-consumer-group

  • 建立工程:複制前面的提供者工程 04-consumer-version,并更名為 05-consumer-group。
  • 修改配置檔案:
<-- ...略 -->
<!--  訂閱服務:指定調用微信支付服務 -->
<dubbo:reference id="weixin" group="pay.weixin" interface="com.yw.dubbo.example.service.SomeService" />

<!--  訂閱服務:指定調用支付寶支付服務 -->
<dubbo:reference id="zhifubao" group="pay.zhifubao" interface="com.yw.dubbo.example.service.SomeService"/>
           
  • 修改消費者類:
public class ConsumerRun {
    public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-consumer.xml");

        // 使用微信支付
        SomeService weixinService = (SomeService) ac.getBean("weixin");
        String weixin = weixinService.hello("China");
        System.out.println(weixin);

        // 使用支付寶支付
        SomeService zhifubaoService = (SomeService) ac.getBean("zhifubao");
        String zhifubao = zhifubaoService.hello("China");
        System.out.println(zhifubao);
    }
}
           

多協定支援

  • 除了 Dubbo 服務暴露協定外,Dubbo架構還支援另外 8 種服務暴露協定:RMI協定、Hessian協定、HTTP協定、WebService協定、Thrift協定、Memcached協定、Redis協定、Rest協定。但在實際生産中,使用最多的就是 Dubbo 服務暴露協定。

各個協定的特點

  • 大資料小并發用短連接配接協定,小資料大并發用長連接配接協定。

dubbo協定

  • Dubbo預設傳輸協定
  • 連接配接個數:單連接配接
  • 連接配接方式:長連接配接
  • 傳輸協定:TCP
  • 傳輸方式:NIO異步傳輸
  • 适用範圍:傳入傳出參數資料包較小(建議小于100K),消費者比提供者個數多,單個消費者無法壓滿提供者,盡量不要用 dubbo 協定傳輸大檔案或超大字元串。

rmi協定

  • 采用 JDK 标準的 java.rmi.* 實作
  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:TCP
  • 傳輸方式:BIO同步傳輸
  • 使用範圍:傳入傳出參數資料包大小混合,消費者與提供者個數差不多,可傳檔案。

hession協定

  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:HTTP
  • 傳輸方式:BIO同步傳輸
  • 使用範圍:傳入傳出參數資料包較大,提供者比消費者個數多,提供者抗壓能力較大,可傳檔案

http協定

  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:HTTP
  • 傳輸方式:BIO同步傳輸
  • 使用範圍:傳入傳出參數資料包大小混合,提供者比消費者個數多,可用浏覽器檢視,可用表單或URL傳入參數,暫不支援傳檔案。

webService協定

  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:HTTP
  • 傳輸方式:BIO同步傳輸
  • 使用範圍:系統內建,跨語言調用

thrift協定

  • thrift 是 Facebook 捐給 Apache 的一個 RPC 架構,其消息傳遞采用的協定即為 thrift 協定。目前 dubbo 支援的 thrift 協定是對 thrift 原生協定的擴充。thrift 協定不支援 null 值的傳遞。

memcached協定與redis協定

  • 它們都是高效的 KV 緩存伺服器。它們會對傳輸的資料使用相應的技術進行緩存。

rest協定

  • 若需要開發具有 Restful 風格的服務,則需要使用該協定。

用法

  • 對于多協定的用法有兩種,一種是同一個服務支援多種協定,一種是不同的服務使用不同的協定。

同一服務支援多種協定

  • 應用場景:系統在使用過程中其使用場景逐漸發生了變化,例如,由原來的消費者數量多于提供者數量,變為消費者數量與提供者數量差不多了,并且原來系統不用傳輸檔案,現在的系統需要傳輸檔案了。此時就将原來預設的 dubbo 協定更換為 rmi 協定。目的是為了相容老工程,擴充新功能。
  • 修改提供者配置檔案:直接在 04-provider-version 工程中進行修改。在提供者中首先要先聲明新添加的協定,然後再服務 <dubbo:service /> 标簽中再增加該新添加的協定。若不指定,預設為 dubbo 協定。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 這裡需要了解這個服務暴露協定的意義。其是指出,消費者要連接配接目前的服務,就需要通過這裡指定的協定及端口号進行通路。這裡的端口号可以是任意的,不一定非要使用預設的端口号(Dubbo預設為20880,rmi預設為1099)。這裡指定的協定名稱及端口号,在目前服務注冊到注冊中心時會一并寫入到服務映射表中。當消費者根據服務名稱查找到相應的主機時,其同時會查詢出消費此服務的協定、端口号等資訊。其底層就是一個 Socket 程式設計,通過主機名和端口号進行連接配接。
  • 修改消費者配置檔案:直接在 04-consumer-version 工程中進行修改。在消費者引用服務時要指出所要使用的協定:
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

不同服務使用不同協定

  • 應用場景:同一個系統中不同的業務具有不用的特點,是以它們的傳輸協定就應該根據它們的特點選擇不同的協定。例如,對于前面使用服務分組實作的“微信支付”與“支付寶支付”,就可以針對不同支付方式,使用不同的協定。
  • 修改提供者配置檔案:直接在 05-provider-group 工程中進行修改。在提供者中首先要先聲明新添加的協定,然後再服務 <dubbo:service /> 标簽中通過 protocol 屬性指定所要使用的服務協定。若不指定,預設為 dubbo 協定。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇
  • 修改消費者配置檔案:直接在 05-consumer-group 工程中進行修改。然後在消費者端通過 <dubbo:reference/> 引用服務時通過添加 protocol 屬性指定要使用的服務協定。
JavaEE 企業級分布式進階架構師(九)Dubbo(2.7)學習筆記(1)Dubbo概述Dubbo入門篇

繼續閱讀