《分佈式系統原理與泛型》定義:java
分佈式系統是若干獨立計算機的集合,這些計算機對於用戶來講就像單個相關係統(好比京東商城等)git
分佈式系統(distributed system)是創建在網絡之上的軟件系統程序員
隨着互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已沒法應對,分佈式服務架構以及流動計算架構勢在必行,急需一個治理系統確保架構有條不亂的演進。github
當網站流量很小是,只需一個應用,將全部功能都部署在一塊兒,以減小部署節點和成本,此時,用於簡化增刪改查工做量的數據訪問框架(ORM)是關鍵。web
單一應用架構有易於開發、易於測試、易於部署的優勢,可是也有很明顯的缺陷,那就是不易擴展、不易於協同開發,後期維護等缺陷spring
當隨着應用規模不斷擴大,單體應用放在一個服務器上,那麼單臺服務器的壓力就扛不住了,這個時候就須要垂直應用架構。就是將單體應用架構拆分紅一個個小應用,並且每個應用都是完整的,當某一塊應用訪問量比較大時,就能夠多放幾臺服務器上。apache
優勢是分工合做容易,每一個人只負責本身的模塊,互不干擾,性能擴展容易等。缺點是界面和業務邏輯不能分離,不能徹底理想的獨立等缺點。windows
RPC[Remote Procedure Call]是指遠程過程調用,是一種進程間通訊方式。他是一種技術的思想,而不是規範。它容許程序調用另外一個地址空間(一般是共享網絡的另外一臺機器上)的過程或函數,而不用程序員顯式編碼這個遠程調用的細節。即程序員不管是調用本地的仍是遠程的函數,本質上編寫的調用代碼基本相同。瀏覽器
RPC的核心就是起到一個網絡鏈接的做用,將倆個服務器進行通訊緩存
RPC框架有不少,如:dubbo、gRPC、Thrift、HSF等
2.1)簡介
Apache Dubbo(incubating)是一款高性能、輕量級的開源javaRPC框架、它提供了三大核心能力:面向接口的遠程調用,智能容錯和負載均衡,以及服務自動註冊和發展
2.2)dubbo的前世此生
Dubbo爲阿里巴巴公司開發一款高性能Java RPC框架
其於2011年將其開源到GitHub上,可是在2014年10年月份,發佈其Dubbo2.4.11版本後,就中止更新了
有不少公司在使用Dubbo,比較出名的有當當網在基於Dubbo的基礎上開發了自身的RPC框架DubboX;網易考拉也基於Dubbo開發了其自身的RPC框架DubboK
2017年SpringCloud開始發力搶佔分佈式市場,阿里巴巴見此狀,又開始更新Dubbo
2018年1月阿里巴巴將DubboX與Dubbo合併,發佈Dubbo2.6
2018年2月15號,阿里巴巴將Dubbo貢獻給Apache
2.4)Zookeeper註冊中心
在windows上安Zookeeper
下載Zookeeper.使用的版本爲zookeeper-3.4.11.tar.gz
解壓縮zookeeper-3.4.11.tar.gz
進入到zookeeper-3.4.11\conf目錄, 複製zoo_sample.cfg, 並從新命名我zoo.cfg
進入到zookeeper-3.4.11新建目錄data
dataDir=../data
E:\zookeeper-3.4.11\bin>zkServer.cmd
7.新開cmd窗口, 啓動zk客戶端驗證是否正常
E:\zookeeper-3.4.11\bin>zkCli.cmd
8.列出節點, 建立新節點, 查詢特定節點的值
[zk: localhost:2181(CONNECTED) 2] ls / 列出節點 [zookeeper] [zk: localhost:2181(CONNECTED) 3] create -e /atguigu 123456 建立節點 Created /atguigu [zk: localhost:2181(CONNECTED) 4] ls / [zookeeper, atguigu] [zk: localhost:2181(CONNECTED) 5] get /atguigu 查詢特定節點的值 123456 cZxid = 0x2 ctime = Sun Aug 11 15:24:52 CST 2019 mZxid = 0x2 mtime = Sun Aug 11 15:24:52 CST 2019 pZxid = 0x2 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x100c7723f2b0000 dataLength = 6 numChildren = 0 [zk: localhost:2181(CONNECTED) 6]
從https://github.com/locationbai/incubator-dubbo-ops-master上clone下源碼,而後進入到incubator-dubbo-ops-master-master\dubbo-admin目錄中,修改
incubator-dubbo-ops-master-master\dubbo-admin\src\main\resources目錄下的application.properties文件內容(主要是Zookeeper的地址要正確)
server.port=7001 spring.velocity.cache=false spring.velocity.charset=UTF-8 spring.velocity.layout-url=/templates/default.vm spring.messages.fallback-to-system-locale=false spring.messages.basename=i18n/message spring.root.password=root spring.guest.password=guest dubbo.registry.address=zookeeper://127.0.0.1:2181
從源碼構建出admin的jar包
mvn clean package
而後經過java命令運行build出的jar包
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
而後經過瀏覽器訪問
http://localhost:7001
用帳戶名稱root,密碼爲root進行登陸
搭建gmall-interface項目用於存放公共的bean和接口
搭建order-service-consumer做爲服務消費者
搭建user-service-provider做爲服務提供者
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="provider"></dubbo:application> <!-- 使用multicast廣播註冊中心暴露服務地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!--當前服務發佈所依賴的協議;webserovice、Thrift、Hessain、http--> <dubbo:protocol name="dubbo" port="20880"/> <!--服務發佈的配置,須要暴露的服務接口--> <dubbo:service interface="com.yueyue.service.UserService" ref="providerService"/> <!--Bean bean定義--> <bean id="providerService" class="com.yueyue.service.impl.UserServiceImpl"/> </beans>
啓動類MainApplication:
public static void main(String[] args) throws IOException { //加載xml配置文件啓動 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml"); context.start(); System.in.read(); // 按任意鍵退出 }
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.example</groupId> <artifactId>user-service-provider</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.example</groupId> <artifactId>interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.6</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.10</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.5</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build> </project>
這裏注意看下結構,不要放亂了。
consimer.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd "> <!-- 消費方應用名,用於計算依賴關係,不是匹配條件,不要與提供方同樣 --> <dubbo:application name="order-service-consumer" /> <!-- 使用multicast廣播註冊中心暴露發現服務地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 生成遠程服務代理,能夠和本地bean同樣使用demoService --> <dubbo:reference id="UserService" interface="com.yueyue.service.UserService" /> </beans>
主啓動方法:
public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("consumer.xml"); OrderService orderService = context.getBean(OrderService.class); orderService.initOrder("1"); System.in.read(); }
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.example</groupId> <artifactId>order-service-consumer</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.example</groupId> <artifactId>interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.6</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.10</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.5</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build> </project>
3.4)抽取公共bean和接口
OrderService接口:
public interface OrderService { void initOrder(String userId); }
UserService接口:
public interface UserService { List<UserAddress> getUserAddressList(String userId); }
當服務提供者和服務消費者均基於SpringBoot來作開發的話,其配置形式與上面普通的基於Spring的項目來講不一樣之處在於:
<dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>
服務提供者配置示例以下 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
服務消費者配置示例以下 server.port=8081 dubbo.application.name=boot-order-service-consumer dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.monitor.protocol=registry
@Service//暴露服務 @Component public class UserServiceImpl implements UserService
@Service public class OrderServiceImpl implements OrderService { //@Autowired @Reference UserService userService;
相同的配置若是出如今三個位置,優先級從上往下逐漸下降(最上面的覆蓋最下邊的)
若是項目是基於Spring來作的開發,那麼優先級順序爲:
-D > dubbo.xml > dubbo.properties
若是項目是基於SpringBoot來作的開發,那麼優先級爲:
-D > application.properties > dubbo.properties
在默認的狀況下,當服務調用者項目啓動的時候會自動檢查要調用的服務是否正常,若是被調用的服務不正常的話,其本身就會啓動失敗。爲了讓項目能正常啓動,而不去檢查所依賴的服務是否正常,能夠關閉服務檢查。下面的配置片斷就是在服務消費者consumer.xml中關閉了服務檢查
<context:component-scan base-package="com.atguigu.gmall.service.impl"></context:component-scan> <dubbo:application name="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.atguigu.gmall.service.UserService" id="userService" timeout="5000" retries="3" version="*"> <!-- <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>
當服務消費者調用服務提供者服務的時候,會存在超時的狀況;而Dubbo提供爲服務消費者設置調用服務提供者服務的超時時間,也提供了服務提供者爲其自身提供的服務設置超時時間;
對於服務提供者和服務消費者Dubbo均提供瞭如下四種粒度的設置
1 .方法級別
接口級別
服務消費者級別
<!-- timeout="0" 默認是1000ms--> <!-- retries="":重試次數,不包含第一次調用,0表明不重試--> <!-- 冪等(設置重試次數)【查詢、刪除、修改】、非冪等(不能設置重試次數)【新增】 --> <dubbo:reference interface="com.atguigu.gmall.service.UserService" <!-- 調用userService失敗後會重試3次,加上第一次的調用,若調用一直失敗的話,總共會調用4次--> id="userService" timeout="5000" retries="3" version="*"> <!-- <dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method> --> </dubbo:reference>
當一個接口實現,出現不兼容升級時,能夠用版本號過渡,版本號不一樣的服務相互間不引用。
能夠按照如下的步驟進行版本遷移(經過視頻的演示發現,Dubbo對於多版本的支持的易用性上作得很是好)
在低壓力時間段,先升級一半提供者爲新版本
再將全部消費者升級爲新版本
老版本服務提供者配置: <dubbo:service interface="com.foo.BarService" version="1.0.0" /> 新版本服務提供者配置: <dubbo:service interface="com.foo.BarService" version="2.0.0" /> 老版本服務消費者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" /> 新版本服務消費者配置: <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> 若是不須要區分版本,能夠按照如下的方式配置 [1]: <dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
一般狀況才採用遠程服務調用後,對於服務消費者的請求,均是在服務提供者的遠程機器上執行的!經過採用本地存根的形式就能夠將服務消費者請求的一些驗證操做在其本地進行執行,這樣就減小了網絡的傳輸,提升了執行效率
第一種方式:
註解的方式:在java類上使用Dubbo提供的註解
第二種方式:
配置文件方式:在xml配置文件中對Dubbo進行配置,包括服務應用的名稱配置,註冊中心配置,通訊規則,服務提供者信息,服務消費者信息等
第三種方式:
Java Config方法:經過java配置類(在一個java類上使用@Configuration註解)的方式配置註冊中心,通訊規則,服務提供者信息,服務消費者信息等
採用Dubbo進行微服務的架構,若使用Zookeeper做爲註冊中心的話,當註冊中心宕機時,是不影響服務調用的,由於關於服務調用的信息,在服務消費者和服務提供者本地有緩存
Dubbo也提供了直連的方式,使服務調用的雙方直接進行通訊,從而能夠繞過註冊中心
Random LoadBalance
RoundRobin LoadBalance
LeastActive LoadBalance
ConsistentHash LoadBalance
當服務器壓力劇增的狀況下,根據實際業務狀況及流量,對一些服務和頁面有策略的不處理或換種簡單的方法處理,從而釋放服務器資源以保證核心交易正常運轉或高效運轉就是服務降級
Dubbo有本身的容錯機制,以下:
Failfast Cluster 快速失敗,只發起一次調用,失敗當即報錯。一般用於非冪等性的操做,好比新增記錄。 Failsafe Cluster 失敗安全,出現異常時,直接忽略。一般用於寫入審計日誌等操做。 Faiback Cluster 失敗自動恢復,後臺記錄失敗請求,定時重發。一般用於消息通知操做。 Forking Cluster 並行調用多個服務器,只要一個成功即返回。一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。可經過forks=2來設置最大並行數 Broadcast Cluster 廣播調用全部提供者,逐個調用,任意一臺報錯則報錯。一般用於通知全部提供者更新緩存或日誌等本地資源信息。
若是是採用Dubbo微服務框架的話,能夠直接使用Dubbo提供的上面的容錯功能
若是項目採用了Dubbo做爲微服務框架時,在項目中也可使用Hystrix
一次完整的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這些步驟都封裝起來,這些細節對用戶來講是透明的,不可見的。
簡單來講Dubbo進行服務暴露的流程以下:
當項目啓動的時候Dubbo首先經過DubboBeanDefinitionParser來解析xml配置文件(在本例中爲provider.xml)。解析信息就是xml配置的xml元素,例如服務提供者實現的接口信息,註冊中心信息等
當解析完xml配置文件後,有兩種信息尤其關鍵,一個是ServiceBean,一個是ServiceConfig
經過ServiceBean提供的信息(服務提供者相關的一些信息,服務的訪問地址等)Dubbo在底層會啓動Netty服務器從而監聽在此ServiceBean中的服務配置的地址和端口上
在啓動Netty服務器的同時,Dubbo會根據ServiceConfig信息將服務提供者的信息註冊到配置中心(Zookeeper中)
下圖爲Dubbo服務消費者調用遠程服務的流程。大致上與Dubbo進行服務暴露的流程是類似的
服務消費者調用服務提供者的流程簡單來講就是,服務消費者經過配置中心獲取到服務提供者相關的信息後,首先進行一些Filter(下圖中的local,mock,cache判斷。例如cache服務消費者是否啓用了緩存功能)。
而後進行服務調用(一般底層是經過客戶端的方式與服務器端進行通訊。經常使用的客戶端經過下圖能夠看出有Netty,mina等)