Dubbo
1.分布式系統中的相關概念
如果理解了分布式系統的相關概念,那么對於后期我們學習服務框架是很有幫助的。
因為服務框架就是為了解決企業中比較棘手的問題而存在的,有問題必定有需求,而描述需求和問題的就是這些概念。
1.1互聯網項目架構目標
1.1.1什么是互聯網項目
說起互聯網項目,那就不得不提到它的兄弟:傳統項目。
傳統項目和互聯網項目分別是什么呢?
- 傳統項目就是企業內部使用的項目,如CRM、OA、HR、學校選課系統等。
- 互聯網項目則是我們日常生活中使用的項目,如微信、淘寶、B站等。
兩者有什么不同呢?
- 用戶群體不同:
- 傳統項目的用戶群體是企業內部員工
- 互聯網項目的用戶群體是廣大的網民們
- 用戶數量不同:
- 傳統項目所面對的內部員工可能只是幾千幾萬級別的
- 互聯網項目所面對的網民會是上億級別的,這就涉及到高並發的問題了
- 用戶的忍耐力不同:
- 即使傳統項目UI不美觀,反應速度慢,甚至會報404、500,內部員工還是得使用,因為是依靠公司生活的。
- 而假如互聯網項目出現功能異常,網絡用戶可隨時更換其他企業的同類型產品,沒有忍耐這一說。
互聯網項目為了保證用戶不流失,需要提高用戶體驗,而影響用戶體驗的主要有以下四點:
- 美觀:一個漂亮的用戶界面很重要,如果做的界面很丑,用戶就不愛用你家的產品。
- 功能:產品的功能要完善、實用。
- 響應速度:響應速度很重要,如果一個頁面幾十秒才能打開,那么用戶的耐心會消耗光。
- 穩定性:保證系統的穩定,不出現404或者500等錯誤。
以上四點,前兩點和我們關系不大,因為美不美觀是UI、美工說了算的,功能是產品經理說了算的。
后兩點,響應速度和穩定性是我們負責的。穩定性不用說了,響應速度的標准很重要。
衡量一個網站速度是否快:
- 打開一個新頁面一瞬間完成(約0.36秒)
- 頁面內跳轉一剎那間完成(約0.018秒)
1.1.2互聯網項目特點
- 用戶多
- 流量大,並發高
- 海量數據
- 易受攻擊:面向公網,任何人都能訪問
- 功能繁瑣
- 變更快:當出現熱點時,需要快速開發出相應功能,搶占市場
1.1.3互聯網項目的架構目標
面對互聯網項目的特點,在設計項目架構的時候,需要達到怎樣的目標呢?
1.高性能:提供快速的訪問體驗
-
衡量一個網站的性能指標:
-
響應時間:指從開始執行一個請求到最后收到響應數據所花費的總時間
-
並發數:指系統同時能處理的請求數量
- 並發連接數:指客戶端向服務端發起請求,並建立了TCP連接。每秒服務器連接的總TCP數量
- 請求數:也被稱為QPS(Query Per Second)指每秒多少請求
- 並發用戶數:單位時間內可以容納多少用戶訪問系統
- 請求數 >= 連接數
-
吞吐量:指單位時間內系統能處理的請求數量
- QPS:Query Per Second 每秒查詢數
- TPS:Transactions Per Second 每秒事務數
- 一個事務指一個客戶機向服務器發送請求然后服務器作出反應的過程。客戶機在發送請求時開始計時,收到服務器響應后結束計時,以此來計算使用的時間和完成的事務個數。
- 一個頁面的一次訪問,只會形成一個TPS;但一次頁面請求,可能產生多次對服務器的請求,就會有多個QPS。故:
QPS >= 並發連接數 >= TPS
-
2.高可用:保證網站服務一直可以正常訪問,比如說淘寶、京東,必須24小時不間斷提供服務。
3.可伸縮:通過硬件增加/減少,提高/降低處理能力
4.高可擴展性:系統間耦合低,可以便捷地通過新增/移除方式,增加/減少新的功能或者模塊
5.安全性:提供網站安全訪問和數據加密,安全存儲等策略
6.敏捷性:隨需應變,快速響應
1.2集群和分布式
- 集群:很多“人”一起,干一樣的事
- 分布式:很多“人”一起,干不一樣的事。這些不一樣的事,合起來是一件大事
1.2.1生活中的集群和分布式
比如說我開了一個飯店,先雇佣了一個大廚,這個大廚負責洗菜、切菜、炒菜。
隨着飯店的發展,顧客越來越多,大廚一個人力不從心,忙不過來。為了增加效率,我又雇佣了一名廚師。
原來的大廚和現在新雇的范廚師都要洗菜、切菜、炒菜,做的活是一樣的,於是形成了一個集群。
有了這樣的一個集群之后,好處就顯而易見了:
- 高性能:原來的大廚一個小時能做10份菜,新雇的范廚師一小時也能做10份菜,現在我們整個飯店一小時能對外提供20份菜,效率是原來的2倍。
- 高可用:假如范廚師生病了,沒法做菜,那么我們飯店還有原來的大廚能做菜,不會歇業。
又過了一段時間,我去參加了一個管理培訓,發現原來的經營方式效率太低了。
我的廚師貴在炒菜能力上,但現在他們浪費了大量的時間在洗菜和切菜上。為了讓他們專注於炒菜,我又分別為這兩個大廚配了專門洗菜和切菜的人。
現在,范廚師、洗菜的人、切菜的人做的工作是不一樣的,但他們的工作合起來就是做飯這件大事,那么他們三個人之間就構成了一個分布式。同樣,原來的大廚、洗菜的人、切菜的人也構成了一個分布式。
同時,共同洗菜的人和共同切菜的人也構成了相應的集群。
經營一段時間后,我發現只分配兩個人去洗菜還是不太夠用,為了提升總體洗菜的效率,我又雇了一個人來洗菜。
這種操作就是可伸縮,哪個模塊效率低,我就給它多分配資源。
又過了一段時間,市面上新推出了切菜機器人,比人的效率高出幾倍。為了提升切菜效率,我把原來的兩個負責切菜的人開除了,買了兩台切菜機器人負責切菜。
這種操作就是高擴展性,哪個模塊過時了,我就把它替換為新的模塊。
1.2.2互聯網中的集群和分布式
早期單機架構(只有一個大廚):
把項目模塊A、B、C、D都部署在一個web服務器里,對外提供服務。
效率很低,有很多問題。
搭建集群(請多個大廚):
我們搞三個web服務器,每個服務器里都部署一份項目。而為了能對外提供服務,需要一個軟件來做負載均衡,當接收到請求時,自動轉發到節點中去。
搭建集群后,整個項目的效率和可用性都大大提升了。但是目前A、B、C、D模塊都是放在一個服務器里的,伸縮和擴展做起來都比較麻煩。
集群分布式(給大廚分工):
我們在原來的基礎上,把AB和CD模塊分別放到不同服務器里去。如此一來,部署AB模塊的服務器與部署CD模塊的服務器就構成了一個分布式。
同時,整個項目里還存在其他同樣功能的服務器,於是就形成了集群分布式。
集群分布式的好處:
1)可伸縮
如果說部署AB模塊的服務器集群效率不夠高,我們就可以再加一個部署AB模塊的服務器
2)高擴展性
模塊拆分后,模塊間耦合度大大降低了。如果現在需要新加一個模塊E,我們只需要在項目里加入一個部署了E模塊的服務器即可,對其他模塊沒有影響。
總結集群和分布式:
- 集群:
- 很多“人”一起,干一樣的事
- 一個業務模塊,部署在多台服務器上
- 分布式:
- 很多“人”一起,干不一樣的事。這些不一樣的事,合起來是一件大事
- 一個很大的業務系統,拆分稱小的業務模塊,分別部署在不同的機器上
1.3架構演進
架構的演進:
1.3.1單體架構
單體架構就是把項目模塊A、B、C、D都部署在一個服務器里,對外提供服務。
當然,這里說的單體架構只是不拆分業務模塊的意思,我們還可以對其搭建集群。如果不搭建集群,那么就是單機架構。
單體架構的優點:開發和部署都很簡單,小項目首選
單體架構的缺點:
- 項目啟動慢:所有模塊都放在一塊,負荷高。
- 可靠性差:如果項目里一個模塊掛了,其他模塊也會跟着掛。
- 可伸縮性差:如果C模塊的訪問量大,需要提升C模塊的效率,那么就只能再添加一個部署了所有模塊的服務器。
- 擴展性和可維護性差:如果想要添加新的功能模塊,就只能和原有的所有模塊放在一起,添加的過程會影響其他所有模塊。
- 性能低:同時運行所有模塊肯定沒有運行單個模塊的效率高。
1.3.2垂直架構
垂直架構就是將單體架構里的多個模塊拆分成多個獨立的項目,形成多個獨立的單體架構。
當然,我們不能將其稱之為分布式,因為項目與項目之間是獨立的,不進行交互的。

垂直架構的優點:
- 項目啟動快:拆分模塊后,單個項目啟動的負荷變小。
- 可靠性強:即使有模塊掛了,也不會影響其他項目里的模塊。
- 可伸縮性強:如果某個模塊效率低,可以對其搭建集群。
- 擴展性和可維護性高:如果想要添加新的功能模塊,不會影響過多其他模塊。
- 性能高:單個項目運行的模塊量小了,效率自然就高了。
垂直架構的缺點:重復功能太多
假如說原本單體架構時,ABCD四個模塊都需要訪問數據庫,就需要加入一個數據訪問模塊E
而改成垂直架構后,分離的AB模塊項目和CD模塊項目還是需要訪問數據庫,就需要在兩個項目中都加一個模塊E

這樣就有了重復模塊,當獨立的項目變多時,會造成極大的資源浪費。
1.3.3分布式架構
分布式架構是在垂直架構的基礎上,將公共業務模塊抽取出來,作為獨立的服務,供其他調用者消費,以實現服務的共享和重用。其中,服務消費者是通過RPC來調用服務提供者的。
RPC: Remote Procedure Call 遠程過程調用。有非常多的協議和技術都實現了RPC的過程。比如:HTTP REST風格,Java RMI規范、WebService SOAP協議、Hession等等。
我們要學的Dubbo框架就是用於實現RPC的。

分布式架構解決了垂直架構重復功能多的問題,但也有缺點:
服務提供者一旦變更,所有服務消費者都要隨着變更。

1.3.4SOA架構
現在有A到F六個服務,服務之間需要相互調用。
如果按照分布式架構,當一個服務改變時,會影響到其他服務

那么我們就不讓它們相互調用了,在所有服務之間,設置一個總線ESB,讓所有服務都通過這個ESB來通信。

SOA:(Service-Oriented Architecture,面向服務的架構)是一個組件模型,它將應用程序的不同功能單元(稱為服務)進行拆分,並通過這些服務之間定義良好的接口和契約聯系起來。
ESB:(Enterparise Servce Bus) 企業服務總線,服務中介。主要是提供了一個服務於服務之間的交互。ESB 包含的功能如:負載均衡,流量控制,加密處理,服務的監控,異常處理,監控告急等等。
ESB就好比是一個中介,當服務A要調用服務D時,會訪問ESB,ESB會找到服務D的地址並將服務A的請求轉發給服務D。
SOA架構解決了分布式架構的問題,當服務提供者發生變更時,其他服務不需要改變。
1.3.5微服務架構
微服務架構是在 SOA 上做的升華,微服務架構強調的一個重點是“業務需要徹底的組件化和服務化”,原有的單個業務系統會拆分為多個可以獨立開發、設計、運行的小應用。這些小應用之間通過服務完成交互和集成。
微服務架構 = 80%的SOA服務架構思想 + 100%的組件化架構思想 + 80%的領域建模思想

特點:
- 服務實現組件化:開發者可以自由選擇開發技術。也不需要協調其他團隊
- 服務之間交互一般使用REST API
- 去中心化:每個微服務有自己私有的數據庫持久化業務數據
- 自動化部署:把應用拆分成為一個一個獨立的單個服務,方便自動化部署、測試、運維
1.3.5總結
- Dubbo是SOA時代的產物
- SpringCloud是微服務時代的產物
2.Dubbo概述
2.1Dubbo的概念
Apache Dubbo 是一款高性能、輕量級的開源Java RPC框架。
提供了六大核心能力:面向接口代理的高性能RPC調用,智能容錯和負載均衡,服務自動注冊和發現,高度可擴展能力,運行期流量調度,可視化的服務治理與運維。
Dubbo是一個分布式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。
官網地址:https://dubbo.apache.org/zh/
特性:
2.2Dubbo的架構

- Container:服務運行容器,負責啟動、加載、運行服務提供者。
- Provider:服務提供者,暴露服務的服務提供方。服務提供者在啟動時,向注冊中心注冊自己提供的服務。
- Consumer:服務消費者,調用遠程服務的服務消費方。服務消費者在啟動時,向注冊中心訂閱自己需要的服務。服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
- Register:注冊中心,返回服務提供者的地址列表給消費者。如果有變更,注冊中心將基於長連接推送變更數據給消費者。
- Monitor:監控中心,監控調用服務的次數。服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。
流程描述:
- 服務提供者向注冊中心注冊自己提供的服務
- 服務消費者需要調用服務提供者的服務,但是不能直接調用,需要先從注冊中心訂閱自己需要的服務
- 注冊中心接收到服務消費者的訂閱后,返回服務消費者需要調用的服務提供者地址
- 服務消費者接收到地址后,根據地址調用服務提供者。調用的過程使用的就是RPC,Dubbo框架內部已經實現好了,我們直接使用Dubbo即可。
3.Dubbo快速入門
3.1安裝Zookeeper服務器
Zookeeper服務器可作為Dubbo架構的注冊中心使用,這也是官方推薦的。
ZooKeeper服務器需要部署在linux系統上。
ZooKeeper服務器是用Java創建的,它運行在JVM之上,需要在linux系統上安裝jdk1.8及以上版本。
3.1.1在linux上安裝jdk
1.准備相關軟件
1)本人使用的是linux系統是:CentOS-6.7-i386
鏈接:https://pan.baidu.com/s/1LIFL80C2vD4HBJCy-MJ-VQ
提取碼:2333
掛載的虛擬機密碼:itcast
2)使用的jdk是:jdk-8u5-linux-i586.tar.gz
鏈接:https://pan.baidu.com/s/1Wm6UAxlNWPWoCz08k0fgoQ
提取碼:2333
官網下載地址:https://www.oracle.com/java/technologies/downloads/#java8
3)使用的遠程訪問工具是:finalshell
下載地址:https://www.jb51.net/softs/717120.html
2.上傳jdk到Linux虛擬機
開啟虛擬機,查詢ip地址,使用finalshell連接到虛擬機,對虛擬機進行操作。
在linux的/usr/local
目錄下新建一個文件夾,命名為jdk,
把jdk文件上傳到linux的/usr/local/jdk
目錄下
3.卸載linux自帶的jdk
linux系統在安裝時自帶了open-jdk,不過我們不需要,卸載后安裝我們自己的。
查看jdk版本:
java -version
查看已安裝的jdk信息:
rpm -qa | grep java
卸載jdk:
rpm -e --nodeps java-1.6.0-openjdk-1.6.0.35-1.13.7.1.el6_6.i686
rpm -e --nodeps java-1.7.0-openjdk-1.7.0.79-2.5.5.4.el6.i686
4.解壓縮上傳的jdk文件
我們在linux上安裝的軟件通常都放在/usr/local
目錄下,由於我們之前直接把jdk文件上傳到了該目錄下,所以不需要再將jdk文件轉移到這里了。
轉移文件命令(切換到文件所在目錄):
mv jdk-8u5-linux-i586.tar.gz /usr/local/jdk/
切換到/usr/local/jdk
,也就是jdk所在目錄,然后直接解壓:
tar -zxvf jdk-8u5-linux-i586.tar.gz
5.配置jdk環境變量
1)打開profile文件
vim /etc/profile
2)在末尾添加如下命令:
JAVA_HOME=/usr/local/jdk/jdk1.8.0_05
CLASSPATH=.:$JAVA_HOME/lib.tools.jar
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME CLASSPATH PATH
保存退出
3)刷新profile文件,使profile文件立即生效
source /etc/profile
3.1.2在linux上安裝ZooKeeper
1.准備ZooKeeper
鏈接:https://pan.baidu.com/s/1TZ1H3rSUVYawIEzTVOPkqg
提取碼:2333
2.上傳ZooKeeper到linux虛擬機
1)在/opt目錄下創建zookeeper目錄
cd /opt#切換到opt目錄
mkdir zookeeper#新建zookeeper目錄
2)將ZooKeeper文件上傳到/opt/zookeeper目錄下
3.解壓
將tar包解壓到/opt/zookeeper目錄下
tar -zxvf apache-zookeeper-3.5.6-bin.tar.gz
4.配置
1)進入到解壓縮后的conf目錄下,拷貝zoo_sample.cfg為zoo.cfg
#進入到conf目錄
cd /opt/zookeeper/apache-zookeeper-3.5.6-bin/conf/
#拷貝
cp zoo_sample.cfg zoo.cfg
2)配置zoo.cfg
#打開目錄
cd /opt/zookeeper/
#創建zooKeeper存儲數據目錄
mkdir zkdata
#修改zoo.cfg
vim /opt/zookeeper/apache-zookeeper-3.5.6-bin/conf/zoo.cfg
修改數據存儲目錄:dataDir=/opt/zookeeper/zkdata
保存退出,至此,Zookeeper已安裝完畢。
3.1.3啟動ZooKeeper
1.啟動ZooKeeper
#進入ZooKeeper的bin目錄
cd /opt/zookeeper/apache-zookeeper-3.5.6-bin/bin/
#啟動
./zkServer.sh start
看到上圖表示ZooKeeper成功啟動
2.查看ZooKeeper狀態
./zkServer.sh status
zookeeper啟動成功。standalone代表zk沒有搭建集群,現在是單節點
3.2Dubbo快速入門項目
3.2.1思路分析

我們來看架構圖,作為注冊中心(Registry)的Zookeeper我們已經安裝並啟動好了,只需要實現服務提供方和服務消費方即可。
實現步驟:
- 創建服務提供者Provider模塊
- 創建服務消費者Consumer模塊
- 在服務提供者模塊里編寫UserServiceImpl提供服務
- 在服務消費者模塊里編寫UserController遠程調用UserServiceImpl提供的服務
- 分別啟動兩個服務,測試
服務提供方和服務消費方是兩個項目,但為了方便,我們在idea中將兩者作為兩個模塊先創建好,再把他們改造成Dubbo的方式。
3.2.2創建Spring和SpringMVC整合項目
1.創建一個空項目
2.創建服務提供者模塊和服務消費者模塊
服務提供者:
服務消費者:
3.添加依賴
在dubbo-web模塊和dubbo-service模塊的pom文件里分別添加如下依賴
<properties>
<spring.version>5.1.9.RELEASE</spring.version>
<dubbo.version>2.7.4.1</dubbo.version>
<zookeeper.version>4.0.0</zookeeper.version>
<!--設置jdk-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- servlet3.0規范的坐標 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--spring的坐標-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!--springmvc的坐標-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<!--Dubbo的起步依賴,版本2.7之后統一為rg.apache.dubb -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
</dependencies>
4.在dubbo-service模塊里編寫Service
(1)創建Service接口及其實現類
1)在dubbo-service模塊的main/java目錄下新建com.tsccg.service.UserService
接口,定義一個抽象方法
2)在同級目錄下新建其實現類:impl.UserServiceImpl,實現抽象方法,返回一個“Hello Dubbo!”
使用@Service注解修飾該實現類
(2)創建配置文件
1)在resources目錄下新建一個屬性配置文件:log4j.properties
,添加如下內容
作用是在控制台上打印日志
# DEBUG < INFO < WARN < ERROR < FATAL
# Global logging configuration
log4j.rootLogger=info, stdout,file
# My logging configuration...
#log4j.logger.com.tocersoft.school=DEBUG
#log4j.logger.net.sf.hibernate.cache=debug
## Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=../logs/iask.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
2)在resources目錄下新建spring文件夾,在其中創建Spring配置文件applicationContext.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://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context"
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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--掃描@Service-->
<context:component-scan base-package="com.tsccg.service"/>
</beans>
5.在dubbo-web模塊里編寫Controller
(1)修改dubbo-web為web模塊
由於dubbo-web模塊是一個web模塊,所以需要指定其打包方式為war,然后補全web模塊所缺的目錄及文件
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_4_0.xsd"
version="4.0">
<!-- 1.注冊spring監聽器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 2.注冊SpringMVC中央調度器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定加載的配置文件 ,通過參數contextConfigLocation加載-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
(2)創建配置文件
web.xml里注冊的Spring監聽器和SpringMVC的中央調度器分別需要讀取Spring和SpringMVC的配置文件。
1)指定Spring配置文件
Spring的配置文件位於dubbo-service模塊,dubbo-web模塊需要在其pom文件里添加dubbo-service模塊的坐標才能讀取到Spring配置文件。
2)創建SpringMVC配置文件
在dubbo-web的resources目錄下新建spring/springmvc.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://dubbo.apache.org/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--聲明注解驅動-->
<mvc:annotation-driven/>
<!--掃描Controller對象-->
<context:component-scan base-package="com.tsccg.controller"/>
</beans>
3)同樣,在resources目錄下添加log4j.properties屬性配置文件
# DEBUG < INFO < WARN < ERROR < FATAL
# Global logging configuration
log4j.rootLogger=info, stdout,file
# My logging configuration...
#log4j.logger.com.tocersoft.school=DEBUG
#log4j.logger.net.sf.hibernate.cache=debug
## Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=../logs/iask.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
(3)在main/java目錄下創建處理器類:com.tsccg.controller.UserController
package com.tsccg.controller;
import com.tsccg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: TSCCG
* @Date: 2021/10/21 20:15
*/
@RestController//處理方法返回String時,只會表示字符串
@RequestMapping("/user")
public class UserController {
@Autowired//自動注入dubbo-service模塊里的UserService對象
private UserService service;
@RequestMapping("/sayHello.do")
public String sayHello() {
//調用UserService對象的方法
return service.fun1();
}
}
(4)編寫發送請求頁面
在webapp目錄下新建index.jsp,添加一個超鏈接,發送相對地址請求:user/sayHello.do
(5)發布dubbo-web到Tomcat服務器上
設置端口號為8000,模塊名為dubbo_web
6.測試
先把dubbo-service模塊進行安裝,生成dubbo-web依賴的jar包
開啟服務器,通過瀏覽器發送請求
訪問成功。
以上是Spring和SpringMVC的一個整合,目前由dubbo-web模塊啟動對外服務,依賴於dubbo-service模塊。
這還只是單體架構,因為分布式架構或者SOA架構要求每個服務模塊都能獨立地啟動,獨立地對外提供服務,而目前dubbo-service模塊只是一個java模塊,不能獨立啟動服務。

接下來要做的就是在項目里加入Dubbo,把項目改造成SOA架構。

3.2.3改造dubbo-service模塊為服務提供者
1.添加依賴
在dubbo-service模塊的pom里加入dubbo相關依賴(這一步我們已經做過了,故省略)
<!--Dubbo的起步依賴,版本2.7之后統一為rg.apache.dubb -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
2.將dubbo-service模塊改造成一個web模塊
在pom里指定打包方式為war,並加入webapp及子文件
web.xml內容:
由於我們在dubbo-service模塊里不需要寫Controller,故不需要注冊SpringMVC的中央調度器
<?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_4_0.xsd"
version="4.0">
<!--注冊Spring監聽器:ContextLoaderListener-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
3.使用Dubbo提供的@Service
注解修飾UserServiceImpl類
Spring提供的@Service注解作用:創建該類對象,並放入Spring的IOC容器里。
Dubbo提供的@Service注解作用:將該類提供的方法(服務)對外發布。將訪問的地址(ip,端口,路徑)注冊到注冊中心(zookeeper)里。

4.完成Dubbo的配置
為了能把訪問地址注冊到注冊中心,必須先讓項目知道注冊中心的地址。
在Spring配置文件applicationContext.xml里聲明Dubbo的配置信息:
<?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">
<!--掃描@Service-->
<!-- <context:component-scan base-package="com.tsccg.service"/>-->
<!--Dubbo的配置-->
<!--1.聲明服務名稱-->
<dubbo:application name="dubbo-service">
<dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!--2.聲明注冊中心地址-->
<dubbo:registry address="zookeeper://121.43.144.188:2181"/>
<!--3.聲明dubbo包掃描,掃描@Service注解-->
<dubbo:annotation package="com.tsccg.service.impl"/>
</beans>
4.測試zookeeper能否連接
新建測試類:
public class Test01 {
private static String ip = "192.168.180.132:2181";
private static int session_timeout = 40000;
private static CountDownLatch latch = new CountDownLatch(1);
@Test
public void testZooKeeper() throws IOException, InterruptedException {
ZooKeeper zooKeeper = new ZooKeeper(ip, session_timeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if(watchedEvent.getState() == Event.KeeperState.SyncConnected) {
//確認已經連接完畢后再進行操作
latch.countDown();
System.out.println("已經獲得了連接");
}
}
});
//連接完成之前先等待
latch.await();
ZooKeeper.States states = zooKeeper.getState();
System.out.println(states);
}
}
3.2.4改造dubbo-web模塊為服務消費者
1.添加依賴
在dubbo-web模塊的pom里加入dubbo相關依賴(這一步我們已經做過了,故省略)
<!--Dubbo的起步依賴,版本2.7之后統一為rg.apache.dubb -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${zookeeper.version}</version>
</dependency>
2.使dubbo-web模塊不再依賴dubbo-service模塊
SOA架構中,各個服務模塊是部署到不同計算機里的,模塊之間不可能相互依賴。
所以,我們的dubbo-web模塊也要獨立,刪除pom文件里dubbo-service模塊的坐標。

刪除后,由於dubbo-web模塊里沒有UserService,所以UserController類聲明UserService對象時就找不到接口,爆紅。

同時,web.xml里的Spring監聽器也找不到Spring配置文件。

為了讓程序通過編譯,我們在dubbo-web模塊里定義一個相同的UserService接口:

如此程序就能通過編譯了。

有紅線是因為只是寫了一個接口,沒有實現類,Spring沒有可管理的Service,@Autowired注解在本地容器中也就找不到Bean對象。
如此一來,Spring容器就沒有用處了。刪除web.xml里的Spring監聽器:

3.使用dubbo提供的@Reference遠程注入Service
既然Spring的@Autowired無法實現注入,那么就使用dubbo提供的實現遠程注入的注解:@Reference
@Reference注解作用:
- 從ZooKeeper注冊中心獲取UserService的訪問url
- 根據url進行遠程調用(RPC)
- 將調用結果封裝為一個代理對象,給變量賦值

4.配置dubbo
既然要從ZooKeeper注冊中心獲取url,那么必須告訴dubbo-web模塊ZooKeeper的位置。
在SpringMVC配置文件中,配置dubbo:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" 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://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--掃描@Controller-->
<context:component-scan base-package="com.tsccg.controller"/>
<!--聲明注解驅動-->
<mvc:annotation-driven/>
<!--Dubbo的配置,同dubbo-service的配置一樣-->
<!--1.聲明服務名稱-->
<dubbo:application name="dubbo-web"/>
<!--2.聲明注冊中心地址-->
<dubbo:registry address="zookeeper://121.43.144.188:2181"/>
<!--3.聲明dubbo包掃描,掃描@Controller注解-->
<dubbo:annotation package="com.tsccg.controller"/>
</beans>
至此,已將原本的單體架構改造成了分布式架構。
3.2.5運行測試
分別開啟dubbo-service服務和dubbo-web服務。
但在啟動dubbo-web服務時報錯,說是qos-server無法綁定本地的端口22222。
qos是一個做遠程調用監控的組件,高版本的dubbo會自動啟動這個組件,默認的端口號就是22222。
出現這種問題說明端口號被占用了。
原因是我們在本地啟動了兩個服務,每個服務都會啟動qos,占用22222端口
這種問題將來在開發中不會出現,因為在開發中,不同服務都部署到不同機器里了。
這里的解決方法:修改任何一個服務的qos端口號
重新啟動:
dubbo-service模塊:
dubbo-web模塊:
通過瀏覽器發起請求:
成功訪問到數據。
3.2.6分離公共接口
(1)缺陷分析
我們上面寫的程序還有點小問題。
在解除dubbo-service模塊依賴后,dubbo-web模塊為了讓程序編譯通過,又在自己的模塊里寫了一個一模一樣的UserService接口。
此時,兩個模塊內都有一個UserService接口。
如果整個項目存在很多模塊,有很多公共接口,接口里又有很多方法,那么分別負責不同模塊的開發團隊就很難保持同步。
因此,我們要把公共接口抽離出來,單獨作為一個依賴模塊:
(2)代碼實現
1)創建接口模塊
2)在接口模塊里創建UserService接口
3)在其他模塊里刪除UserService接口並添加dubbo-interface模塊的依賴
4)運行測試
4.Dubbo高級特性
4.1監控中心
4.1.1dubbo-admin簡介

在dubbo架構中,還有一個Monitor,監控中心。
監控中心一般用於統計服務的調用次數,但是官方聲明Monitor還在開發中,仍不完善。
所以我們就用官方提供的另一個功能更強大的工具:dubbo-admin來做監控中心。
dubbo-admin:圖形化的服務管理平台。可以從注冊中心獲取到所有的服務提供者和服務消費者,並進行配置管理:
- 路由規則
- 動態配置
- 服務降級
- 訪問控制
- 權重調整
- 負載均衡等
4.1.2dubbo-admin的安裝
1、環境准備
dubbo-admin 是一個前后端分離的項目。前端使用vue,后端使用springboot,安裝 dubbo-admin 其實就是部署該項目。我們將dubbo-admin安裝到開發環境上。要保證開發環境有jdk,maven,node
安裝node(如果當前機器已經安裝請忽略)
因為前端工程是用vue開發的,所以需要安裝node。
下載地址
https://nodejs.org/en/
2、下載 Dubbo-Admin
進入gitee,搜索dubbo-admin
https://gitee.com/jinhaoliang/dubbo-admin?_from=gitee_search
下載文件到本地
3、把下載的zip包解壓到指定文件夾(解壓到哪個文件夾隨意)
4、修改配置文件
解壓后我們進入…\dubbo-admin-master\dubbo-admin\src\main\resources目錄,找到 application.properties 配置文件 進行配置修改
修改zookeeper地址
5、打包項目
切換到dubbo-admin-master根目錄,按住shift+右鍵,打開powershell窗口,然后執行打包命令
mvn clean package
出現下圖即代表打包成功
6、啟動后端
切換到目錄dubbo-admin-master\dubbo-admin\target
同樣shift+右鍵,打開powershell窗口,執行下面的命令啟動 dubbo-admin
java -jar .\dubbo-admin-0.0.1-SNAPSHOT.jar
出現下圖即表示項目啟動成功
可以訪問 dubbo-admin頁面了,在瀏覽器地址欄輸入如下網址:
http://localhost:7001
彈出登錄框,用戶名密碼都是root,然后就可以登錄到dubbo-admin監控頁面了
4.1.3dubbo-admin的簡單使用
1.監控服務
在上面的步驟中,我們已經進入了Dubbo-Admin的主界面,在【快速入門】例子中,我們定義了服務生產者、和服務消費者,下面我們從Dubbo-Admin管理界面找到這個兩個服務。
開啟dubbo-server和dubbo-web的服務,刷新監控中心頁面:
2.查詢服務
輸入*
查詢所有服務(按服務名/按應用名/按ip地址):
查看服務信息:
20880是dubbo在本地機器占用的一個端口號,當我們要在本地啟動多個服務時,需要設置為不同端口。
設置方式:
<!--修改dubbo服務端口,默認為20880-->
<dubbo:protocol port="20881"/>
點擊ip地址,查看更詳細的信息
4.2dubbo高級特性
4.2.1序列化
當服務消費者調用服務提供者的服務時,如果獲取的是一個對象,那么就需要提供者將該對象序列化成流數據來傳送給消費者,然后消費者再將流數據反序列化為對象。
由於提供者序列化和消費者反序列化的必須是同一個實體類對象,那么就需要將這個公共實體類抽離出來,作為一個單獨的模塊,然后由提供者和消費者共同依賴這個實體類模塊。
我們新創建一個dubbo-pojo模塊,在里面定義實體類User,實現Serializable接口(一個對象要想能被序列化,那么它的類必須實現Serializable接口)
在dubbo-interface、dubbo-service、dubbo-web模塊里引入dubbo-pojo模塊的坐標。
在dubbo-interface模塊的接口里,新定義一個抽象方法,返回值為一個User對象
在dubbo-service模塊的接口實現類里,實現該方法
在dubbo-web模塊的index.jsp里,創建一個表單,攜帶請求參數id
向該模塊的服務器發送user/find.do
的請求
在處理器類UserController里添加一個新方法,用於處理此請求。
該方法返回一個User對象的json格式字符串
測試:
執行dubbo-pojo、dubbo-interface模塊的install,先后開啟dubbo-service、dubbo-web模塊的服務。
通過瀏覽器發送請求:
4.2.2地址緩存
問:注冊中心掛了,服務是否可以正常訪問?
可以。因為dubbo服務消費者在第一次調用時,會將服務提供方地址緩存到本地,以后再調用則不會訪問注冊中心。
當服務提供者地址發生變化時,注冊中心會通知服務消費者。
演示:
1.開啟服務,並調用
2.關閉zookeeper
3.繼續調用服務
仍然能訪問到
4.2.3超時與重試
1、超時
一次正常的訪問流程:
- 用戶發送請求給服務消費者
- 服務消費者創建一個線程,調用服務提供者
- 服務提供者返回服務給消費者
- 消費者接收到服務后,銷毀線程,將處理結果返回給用戶
但當服務消費者在調用服務提供者時,如果發生了阻塞、等待等情況,服務消費者會一直等待下去,線程不會被銷毀,所占用的資源不會被釋放。
請求少時,不會有什么大問題。但當處於某個峰值時刻,大量的請求都在同時請求服務消費者,會造成線程的大量堆積,勢必會造成雪崩。
針對這個問題,dubbo使用了超時機制。設置一個超時時間,如果在時間段內,無法完成服務訪問,會自動斷開連接,銷毀線程。
設置超時時間方式:
在dubbo提供的@Service和@Reference注解里有個timeout屬性,默認值1000,單位毫秒。
代碼演示超時機制:
在服務提供者,dubbo-service模塊的UserServiceImpl類里,設置@Service的timeout屬性值為2000,retries屬性值為0。(retries:調用失敗后的重試次數,稍后講解)
訪問數據庫用了3秒,(用Thread.sleep模擬)
為了能看到秒數,我們在服務消費者,dubbo-web模塊里新開一個線程,每隔一秒在后台輸出一個數
@RestController//處理方法返回String時,只會是字符串
@RequestMapping("/user")
public class UserController {
@Reference
private UserService userService;
int i = 1;//設置初始值
@RequestMapping("/find.do")
public User findUserById(Integer id) {
//新開辟一個線程
new Thread(new Runnable() {
@Override
public void run() {
//設置死循環
while (true) {
//每隔一秒打印一個數
System.out.println(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
return userService.find(id);
}
}
演示:
由於我電腦太渣,數據不太准確,但大體一致。
當然,在服務提供者的@Service和服務消費者的@Reference里都可以設置超時時間,但我們一般都在服務提供者里設置。理由:
- 因為作為服務提供者,知道要訪問數據庫可能會花很長時間,所以能以此延長超時時間。
- 而且@Service和@Reference里同時設置超時時間時,@Reference會覆蓋@Service設置的超時時間。
2、重試
前面我們知道了,當在超時時間內無法完成服務的調用,那么連接就會自動斷開。
如果出現網絡抖動,這次的請求可能就會失敗。
什么是網絡抖動呢?就是網絡有時會突然斷開,然后突然連接上。就好比我們打lol時,有時網會突然斷掉,當網絡斷的時間較長時,就會退出游戲。
為了解決這一問題,dubbo又提供了重試機制。當本次請求失敗后,會重試幾次,如果重試指定次數后,仍然請求失敗,那么才徹底斷開請求。
重試機制通過retries 屬性來設置重試次數,默認為 2 次,連帶第一次發送請求,共3次。
代碼演示:

開啟服務,發送請求:
4.2.4多版本

灰度發布:當出現新功能時,會讓一部分用戶先使用新功能,用戶反饋沒問題時,再將所有用戶遷移到新功能。
那么,如何快速將所有用戶遷移到新功能里去呢?
dubbo中使用version屬性來設置和調用一個接口的不同版本。
代碼演示:
我們在UserServiceImpl類的@Service注解里,設置version屬性值為v1.0
。
然后拷貝UserServiceImpl類為:UserServiceImpl2,作為新版本的服務,設置version屬性值為v2.0
。
在服務消費者的UserController類的@Reference注解里,先設置version屬性值為v1.0
,模擬服務未發布前
開啟服務,發送請求:
此時,修改UserController的@Reference的version屬性為v2.0
,然后重啟dubbo-web服務,實現版本的遷移:
4.2.5負載均衡
當一個服務提供者不夠用時,我們可以對其搭建集群,創建多個提供相同服務的服務提供者。
而這種情況下,服務消費者調用服務時,該調用哪一個服務提供者就是一個問題,也就是負載均衡。

dubbo提供了四種負載均衡策略:
- Random(默認):按權重設置隨機概率。
- RoundRobin:按權重輪詢。
- LeastActive:最少活躍調用數,相同活躍數的隨機
- 當服務消費者調用服務提供者前,先查看它們上一次完成服務訪問花費的時間:
- 如果時間一致,那么隨機調用
- 如果不一致,那么調用時間花費最短的。
- 當服務消費者調用服務提供者前,先查看它們上一次完成服務訪問花費的時間:
- ConsistentHash:一致性Hash,相同參數的請求總是發到同一提供者
AbstractLoadBalance是四種負載均衡策略的抽象類
1、Random:按權重設置隨機概率
按權重設置隨機概率。
當所有服務提供者的權重相同,都為100時,服務消費者隨機調用服務提供者,概率均為1/3
當第二個服務提供者的權重為200時,調用它的概率為1/2,調用其他服務提供者的概率為1/4
代碼演示:
1)再創建兩個相同的服務提供者
2)開啟第一個服務提供者的服務
修改UserServiceImpl的fun1()方法返回1...
然后啟動dubbo-service服務

監控中心:
3)開啟第二個服務提供者的服務
修改fun1方法的返回值為2...
修改qos和dubbo端口號,然后啟動dubbo-service (1)
監控中心:
4)開啟第三個服務提供者
按同樣方法開啟第三個服務提供者,但設置權重為200
修改端口號:
修改權重:
在dubbo提供的@Service注解里設置weight屬性值為200
監控中心:
當然,也可以通過監控中心dubbo-admin修改權重:
6)開啟服務消費者:dubbo-web的服務
設置@Reference的loadbalance屬性值為random(默認),啟動服務
多次發送請求,查看調用情況:
調用2的概率居多
2、RoundRobin:按權重輪詢
輪詢就是挨個調用。
當權重都一樣時,按順序1、2、3的調用
當2的權重為200時,按順序1、2、3、2的順序調用
修改loadBalance屬性值為:roundrobin,然后重啟dubbo-web服務
設置所有服務提供者權重相同:
連續發送請求:
設置3的權重為200:
重新多次發送請求:
4.2.6集群容錯

集群容錯模式:
- Failover Cluster:失敗重試。默認值。當出現失敗,重試其它服務器 ,默認重試2次,使用 retries 配置。一般用於讀操作
- Failfast Cluster :快速失敗,只發起一次調用,失敗立即報錯。通常用於寫操作。
- Failsafe Cluster :失敗安全,出現異常時,直接忽略,返回一個空結果。通常用於日志。
- Failback Cluster :失敗自動恢復,后台記錄失敗請求,定時重發。通常用於消息通知操作。
- Forking Cluster :並行調用多個服務器,只要一個成功即返回。
- Broadcast Cluster :廣播調用所有提供者,逐個調用,任意一台報錯則報錯。
4.2.7服務降級

機器B提供了三個服務:廣告、日志、支付。其中支付服務最重要。
假如當A調用機器B的支付服務時,機器B性能到達了極限,CPU利用率達到了100%,馬上就要崩潰了
那么為了能保證支付服務能正常被調用,需要將其他的相對不重要的廣告和日志服務停掉。這就是服務降級。
服務降級方式:
1.強制返回null
消費方對該服務的方法調用都直接返回null值,不發起遠程調用
@Reference(mock=focus:return null)
用於屏蔽不重要的服務不可用時對調用方法的影響。
2.失敗返回null
消費方對該服務的方法調用在失敗后,再返回null值,不拋出異常。
@Reference(mock=fail:return null)
用於容忍不重要的服務不穩定時對調用方的影響。