1、分佈式基礎理論
一、什麼是分佈式系統?
《分佈式系統原理與範型》定義:前端
「分佈式系統是若干獨立計算機的集合,這些計算機對於用戶來講就像單個相關係統」java
分佈式系統(distributed system)是創建在網絡之上的軟件系統。git
隨着互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已沒法應對,分佈式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。程序員
二、發展演變
單一應用架構
當網站流量很小時,只需一個應用,將全部功能都部署在一塊兒,以減小部署節點和成本。此時,用於簡化增刪改查工做量的數據訪問框架(ORM)是關鍵。github
適用於小型網站,小型管理系統,將全部功能都部署到一個功能裏,簡單易用。web
缺點:一、性能擴展比較難,比喻新增功能,修改功能,每臺機子都要從新部署算法
二、協同開發問題,不少人都在同一個功能模塊裏面作,容易亂spring
3、不利於升級維護,功能增長超量,項目大小增長,單臺機子就承受不住apache
垂直應用架構
當訪問量逐漸增大,單一應用增長機器帶來的加速度愈來愈小,將應用拆成互不相干的幾個應用,以提高效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。界面和業務邏輯是在一塊兒。windows
經過切分業務來實現各個模塊獨立部署,下降了維護和部署的難度,團隊各司其職更易管理,性能擴展也更方便,更有針對性。
缺點: 公用模塊沒法重複利用,開發性的浪費
分佈式服務架構
當垂直應用愈來愈多,應用之間交互不可避免,將核心業務抽取出來,做爲獨立的服務,逐漸造成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提升業務複用及整合的分佈式服務框架(RPC)是關鍵。
流動計算架構
當服務愈來愈多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增長一個調度中心基於訪問壓力實時管理集羣容量,提升集羣利用率。此時,用於提升機器利用率的資源調度和治理中心(SOA)[ Service Oriented Architecture]是關鍵。
三、RPC
什麼叫RPC
RPC【Remote Procedure Call】是指遠程過程調用,是一種進程間通訊方式,他是一種技術的思想,而不是規範。它容許程序調用另外一個地址空間(一般是共享網絡的另外一臺機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即程序員不管是調用本地的仍是遠程的函數,本質上編寫的調用代碼基本相同。
RPC基本原理
RPC兩個核心模塊:通信,序列化。
一次完整的RPC調用流程(同步調用,異步另說)以下:
1)服務消費方(client)調用以本地調用方式調用服務;
2)client stub接收到調用後負責將方法、參數等組裝成可以進行網絡傳輸的消息體;
3)client stub找到服務地址,並將消息發送到服務端;
4)server stub收到消息後進行解碼;
5)server stub根據解碼結果調用本地的服務;
6)本地服務執行並將結果返回給server stub;
7)server stub將返回結果打包成消息併發送至消費方;
8)client stub接收到消息,並進行解碼;
9)服務消費方獲得最終結果。
RPC框架的目標就是要2~8這些步驟都封裝起來,這些細節對用戶來講是透明的,不可見的。
2、dubbo核心概念
一、簡介
Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調用,智能容錯和負載均衡,以及服務自動註冊和發現。
二、基本概念
服務提供者(Provider):暴露服務的服務提供方,服務提供者在啓動時,向註冊中心註冊本身提供的服務。
服務消費者(Consumer): 調用遠程服務的服務消費方,服務消費者在啓動時,向註冊中心訂閱本身所需的服務,服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。
註冊中心(Registry):註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者
監控中心(Monitor):服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心
容器(Container):服務運行容器
調用關係說明
- 服務容器負責啓動,加載,運行服務提供者。
- 服務提供者在啓動時,向註冊中心註冊本身提供的服務。
- 服務消費者在啓動時,向註冊中心訂閱本身所需的服務。
- 註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者。
- 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。
- 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。
Dubbo 架構具備如下幾個特色,分別是連通性、健壯性、伸縮性、以及向將來架構的升級性。
三、dubbo環境搭建
【windows】-安裝zookeeper
1、下載zookeeper
網址 https://archive.apache.org/dist/zookeeper/zookeeper-3.4.13/
2、解壓zookeeper
解壓運行zkServer.cmd ,初次運行會報錯,沒有zoo.cfg配置文件
3、修改zoo.cfg配置文件
將conf下的zoo_sample.cfg複製一份更名爲zoo.cfg便可。
注意幾個重要位置:
dataDir=./ 臨時數據存儲的目錄(可寫相對路徑)
clientPort=2181 zookeeper的端口號
修改完成後再次啓動zookeeper
4、使用zkCli.cmd測試
ls /:列出zookeeper根下保存的全部節點
create –e /chen 123:建立一個臨時chen節點,值爲123
get /chen:獲取/chen節點的值
5、流程說明:
服務提供者啓動時: 向 /dubbo/com.foo.BarService/providers 目錄下寫入本身的 URL 地址
服務消費者啓動時: 訂閱 /dubbo/com.foo.BarService/providers 目錄下的提供者 URL 地址。並向 /dubbo/com.foo.BarService/consumers 目錄下寫入本身的 URL 地址
監控中心啓動時: 訂閱 /dubbo/com.foo.BarService 目錄下的全部提供者和消費者 URL 地址。
6、支持如下功能:
當提供者出現斷電等異常停機時,註冊中心能自動刪除提供者信息
當註冊中心重啓時,能自動恢復註冊數據,以及訂閱請求
當會話過時時,能自動恢復註冊數據,以及訂閱請求
當設置 <dubbo:registry check="false" /> 時,記錄失敗註冊和訂閱請求,後臺定時重試
可經過 <dubbo:registry username="admin" password="1234" /> 設置 zookeeper 登陸信息
可經過 <dubbo:registry group="dubbo" /> 設置 zookeeper 的根節點,不設置將使用無根樹
支持 * 號通配符 <dubbo:reference group="*" version="*" />,可訂閱服務的全部分組和全部版本的提供者
【windows】-安裝dubbo-admin管理控制檯
dubbo自己並非一個服務軟件。它其實就是一個jar包可以幫你的java程序鏈接到zookeeper,並利用zookeeper消費、提供服務。因此你不用在Linux上啓動什麼dubbo服務。
可是爲了讓用戶更好的管理監控衆多的dubbo服務,官方提供了一個可視化的監控程序,不過這個監控即便不裝也不影響使用。
1、下載dubbo-admin
https://github.com/apache/incubator-dubbo-ops
2、進入目錄,修改dubbo-admin配置
修改 src\main\resources\application.properties 指定zookeeper地址
3、打包dubbo-admin
mvn clean package -Dmaven.test.skip=true
4、運行dubbo-admin
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
默認使用root/root 登錄
四、dubbo-helloworld
提出需求
某個電商系統,訂單服務須要調用用戶服務獲取某個用戶的全部地址;
咱們如今 須要建立兩個服務模塊進行測試 模塊 |
功能 |
訂單服務web模塊 |
建立訂單等 |
用戶服務service模塊 |
查詢用戶地址等 |
測試預期結果:
訂單服務web模塊在A服務器,用戶服務模塊在B服務器,A能夠遠程調用B的功能。
工程架構
根據 dubbo《服務化最佳實踐》
1、分包
建議將服務接口,服務模型,服務異常等均放在 API 包中,由於服務模型及異常也是 API 的一部分,同時,這樣作也符合分包原則:重用發佈等價原則(REP),共同重用原則(CRP)。
若是須要,也能夠考慮在 API 包中放置一份 spring 的引用配置,這樣使用方,只需在 spring 加載過程當中引用此配置便可,配置建議放在模塊的包目錄下,以避免衝突,如:com/alibaba/china/xxx/dubbo-reference.xml。
2、粒度
服務接口儘量大粒度,每一個服務方法應表明一個功能,而不是某功能的一個步驟,不然將面臨分佈式事務問題,Dubbo 暫未提供分佈式事務支持。
服務接口建議以業務場景爲單位劃分,並對相近業務作抽象,防止接口數量爆炸。
不建議使用過於抽象的通用接口,如:Map query(Map),這樣的接口沒有明確語義,會給後期維護帶來不便。
建立模塊
1、gmall-interface:公共接口層(model,service,exception…)
做用:定義公共接口,也能夠導入公共依賴
Bean模型
public class UserAddress implements Serializable{
private Integer id;
private String userAddress;
private String userId;
private String consignee;
private String phoneNum;
private String isDefault;
}
Service接口
UserService、OrderService
2.boot-user-service-provider用戶模塊(對用戶接口的實現)
做用:做爲提供者,提供接口的實現
pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.gmall</groupId>
<artifactId>gmall-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
Impl實現類

//@Service//使用dubbo提供的Service暴露服務 @Component public class UserServiceImpl implements UserService { @HystrixCommand @Override public List<UserAddress> getUserAddressList(String userId) { System.out.println("UserServiceImpl......."); UserAddress address1 = new UserAddress(1, "深圳市創業投資大廈", "1", "小明", "010-00000000", "Y"); UserAddress address2 = new UserAddress(2, "深圳市怡化金融大廈", "2", "小李", "010-11111111", "N"); System.out.println(address1); return Arrays.asList(address1,address2); } }
provider.xml

<!-- 一、指定當前服務/應用的名字(一樣的服務名字相同,不要和別的服務同名) --> <dubbo:application name="boot-user-service-provider"></dubbo:application> <!-- 二、指定註冊中心的位置 --> <!-- <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry> --> <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"></dubbo:registry> <!-- 三、指定通訊規則(通訊協議?通訊端口) --> <dubbo:protocol name="dubbo" port="20880"></dubbo:protocol> <!-- 四、暴露服務 ref:指向服務的真正的實現對象 --> <dubbo:service interface="com.gmall.service.UserService" ref="userServiceImpl" retries="2" timeout="1000"> <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> </dubbo:service> <!--服務的實現 --> <bean id="userServiceImpl" class="com.gmall.service.impl.UserServiceImpl"></bean> <!-- 開啓dubbo註解支持 --> <!-- 掃描註解包路徑,多個包用逗號分隔,不填pacakge表示掃描當前ApplicationContext中全部的類 --> <!--<dubbo:annotation package="com.gmall.service.impl"/>--> <!--統一設置服務提供方的規則 --> <dubbo:provider timeout="1000" retries="3"></dubbo:provider> <!-- 鏈接監控中心 --> <dubbo:monitor protocol="registry"></dubbo:monitor>
application.properties

#dubbo.application.name=user-service-provider #dubbo.registry.address=127.0.0.1:2181 #dubbo.registry.protocol=zookeeper # #dubbo.protocol.name=dubbo #dubbo.protocol.port=20881 # #dubbo.monitor.protocol=registry #dubbo.scan.base-packages=com.gmall
Application啓動類

/** * 一、導入依賴; * 1)、導入dubbo-starter * 2)、導入dubbo的其餘依賴 * @author chenhao * * SpringBoot與dubbo整合的兩種方式: * 1)、保留dubbo xml配置文件; * 導入dubbo-starter,使用@ImportResource導入dubbo的配置文件便可 * 2)、導入dubbo-starter,在application.properties配置屬性,使用@Service【暴露服務】使用@Reference【引用服務】 */ //@EnableDubbo //開啓基於註解的dubbo功能 //@ImportResource(locations="classpath:provider.xml") //@EnableDubbo @ImportResource(locations="classpath:provider.xml") @EnableHystrix //開啓服務容錯 @SpringBootApplication public class BootUserServiceProviderApplication { public static void main(String[] args) { SpringApplication.run(BootUserServiceProviderApplication.class, args); } }
①、能夠用原始xml配置方式:如上provider.xml中配置,Application啓動類中須要註解引入@ImportResource(locations="classpath:provider.xml");
使用註解方式:使用註解須要先開啓dubbo註解,如provider.xml使用<dubbo:annotation package="com.gmall.service.impl"/>開啓dubbo註解,可是這種方式dubbo官網說下個版本再也不使用,或者在Application啓動類中使用@EnableDubbo註解,使用註解後就不須要再provider.xml中配置暴露服務,只須要在須要暴露的服務類上添加註解@Service //使用dubbo提供的service註解,註冊暴露服務
②、也可使用properties的方式:Application啓動類中不須要引入provider.xml,可是要開啓dubbo註解,而且須要在須要暴露的服務類上添加dubbo提供的service註解,註冊暴露服務
application.name就是服務名,不能跟別的dubbo提供端重複registry.protocol 是指定註冊中心協議
registry.address 是註冊中心的地址加端口號
protocol.name 是分佈式固定是dubbo,不要改。
base-package 註解方式要掃描的包
三、boot-order-service-consumer訂單模塊(調用用戶模塊)
做用:做爲消費者
pom.xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>com.gmall</groupId>
<artifactId>gmall-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Controller

@Controller public class OrderController { @Autowired OrderService orderService; @ResponseBody @RequestMapping("/initOrder") public List<UserAddress> initOrder(@RequestParam("uid")String userId) { return orderService.initOrder(userId); } }
Impl實現類

//@Reference @Autowired UserService userService; //@HystrixCommand(fallbackMethod="hello") @Override public List<UserAddress> initOrder(String userId) { // TODO Auto-generated method stub System.out.println("用戶id:"+userId); //一、查詢用戶的收貨地址 List<UserAddress> addressList = userService.getUserAddressList(userId); return addressList; }
consumer.xml

<dubbo:application name="boot-order-service-consumer"></dubbo:application> <dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry> <!--聲明須要調用的遠程服務的接口;生成遠程服務代理 --> <!-- 1)、精確優先 (方法級優先,接口級次之,全局配置再次之) 2)、消費者設置優先(若是級別同樣,則消費方優先,提供方次之) --> <!-- timeout="0" 默認是1000ms--> <!-- retries="":重試次數,不包含第一次調用,0表明不重試--> <!-- 冪等(設置重試次數)【查詢、刪除、修改】、非冪等(不能設置重試次數)【新增】 --> <dubbo:reference interface="com.gmall.service.UserService" id="userService" timeout="5000" retries="3" > <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> </dubbo:reference> <!-- 配置當前消費者的統一規則:全部的服務都不檢查 --> <dubbo:consumer check="false" timeout="5000"></dubbo:consumer> <dubbo:monitor protocol="registry"></dubbo:monitor> <!-- <dubbo:monitor address="127.0.0.1:7070"></dubbo:monitor> -->
application.properties

#server.port=8081 # #dubbo.application.name=boot-order-service-consumer #dubbo.registry.address=zookeeper://127.0.0.1:2181 #dubbo.monitor.protocol=registry
Application啓動類

//@EnableDubbo //@EnableHystrix @ImportResource(locations="classpath:consumer.xml") @SpringBootApplication public class BootOrderServiceConsumerApplication { public static void main(String[] args) { SpringApplication.run(BootOrderServiceConsumerApplication.class, args); } }
①、能夠用原始xml配置方式:如上consumer.xml中配置,Application啓動類中須要註解引入@ImportResource(locations="classpath:consumer.xml");
使用註解方式:使用註解須要先開啓dubbo註解,在Application啓動類中使用@EnableDubbo註解,使用註解後就不須要再provider.xml中聲明須要調用的遠程服務的接口,只須要在須要引用的服務上UserService userService;添加註解@Reference //使用dubbo提供的reference註解引用遠程服務
②、也可使用properties的方式:Application啓動類中不須要引入consumer.xml,可是要開啓dubbo註解,而且須要在須要引用的服務上UserService userService;添加註解@Reference //使用dubbo提供的reference註解引用遠程服務
五、監控中心
dubbo-admin
圖形化的服務管理頁面;安裝時須要指定註冊中心地址,便可從註冊中心中獲取到全部的提供者/消費者進行配置管理,上面已經安裝
dubbo-monitor-simple
簡單的監控中心
1、安裝
下載 dubbo-ops
https://github.com/apache/incubator-dubbo-ops
修改配置指定註冊中心地址
進入 dubbo-monitor-simple\src\main\resources\conf
修改 dubbo.properties文件
打包dubbo-monitor-simple
mvn clean package -Dmaven.test.skip=true
解壓 tar.gz 文件,並運行start.bat
雙擊運行start.bat
啓動訪問8080
2、監控中心配置
全部服務配置鏈接監控中心,進行監控統計
<!-- 監控中心協議,若是爲protocol="registry",表示從註冊中心發現監控中心地址,不然直連監控中心 -->
<dubbo:monitor protocol="registry"></dubbo:monitor>
Simple Monitor 掛掉不會影響到 Consumer 和 Provider 之間的調用,因此用於生產環境不會有風險。
源碼地址:https://gitee.com/chenhaozi/SpringBoot_dubbo.git