Dubbo是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調用,智能容錯和負載均衡,以及服務自動註冊和發現。java
RPC全稱(Remote Procedure Call)遠程過程調用web
過程指的是某個代碼片斷的執行,遠程調用則意味着咱們能夠在其餘進程,甚至其餘機器上去調用這段代碼,固然也能獲取到其執行後的返回值,按照這個定義,咱們請求某個http地址獲得相應數據其實也算一次RPC,可是這樣的方式太過麻煩,(數據要先打包成http請求格式,在調用相關的請求庫,拿到的結果也是文本格式的須要在進行轉換),執行效率,和開發效率相比RPC則低一些;算法
咱們須要一種更簡單的方式來完成分佈式開發中的RPC環節,這也是Dubbo的核心所在,有多簡單呢? 調用遠程服務器上的某個服務時就像是調用本地的某個方法同樣簡單,就像下面這樣spring
RPC是用來實現分佈式構架的基石,分佈式構架將同一個系統中的不一樣模塊拆分到不一樣的子系統中,而子系統又分佈在不一樣的服務器上,這時就須要RPC在來完成子系統之間的相互訪問;apache
能夠這麼說分佈式少不了RPC,RPC也要在分佈式系統中才能發揮其核心價值;服務器
毫無覺得底層確定是要經過socket來進行網絡通信的,可是如何可以直接調用另外一個機器上的方法呢?網絡
服務消費方(client)調用以本地調用方式調用服務;架構
2)client stub接收到調用後負責將方法、參數等組裝成可以進行網絡傳輸的消息體;併發
3)client stub找到服務地址,並將消息發送到服務端;app
4)server stub收到消息後進行解碼;
5)server stub根據解碼結果調用本地的服務;
6)本地服務執行並將結果返回給server stub;
7)server stub將返回結果打包成消息併發送至消費方;
8)client stub接收到消息,並進行解碼;
9)服務消費方獲得最終結果。
固然傳遞的參數或返回值是某個Java對象時則還須要對其進行序列化與反序列化
集羣構架是將相同的處理邏輯進行復制(複製一份源代碼),建立出一組具有相同功能的服務集合,集羣中每一個服務都可以獨立的完成用戶的請求,它們之間基本上不須要互相通信,也就用不上RPC了;
分佈式指的是將一個系統拆分爲多個獨立的子系統,部署在不一樣的機器上;
在處理任務時會將一個任務拆分紅若干子任務,分發給不一樣的子系統處理,每一個子系統僅能處理一部分任務,一般一個完整的任務包含多個處理步驟,例如用戶要購買某個商品,須要先建立訂單,而後修改庫存,假設修改庫存的服務由另外一個服務器提供這時候RPC就閃亮登場了;
能夠發現分佈式與集羣在底層構架上徹底不一樣,因此要將一個本來集羣的系統重構爲分佈式的話,則須要大量的修改,因此若系統後期存在高併發的需求,則能夠在項目初期就採用分佈式構架來搭建;
分佈式是必要的嗎?
缺點:
由於須要走RPC,響應時間變長
系統構架更加複雜,運維工做麻煩
公共模塊沒法複用(代碼級別)
須要強調的是:分佈式和集羣並非只能二選一,在高併發下場景下還能夠給壓力大的節點組建集羣;
分佈式與微服務:
分佈式系統是多個處理機經過通訊線路互聯而構成的鬆散耦合的系統,是一個更寬泛的概念;
微服務從結構上來看也屬於分佈式,微服務強調的是將某個功能完徹底全的獨立出來,完全的解開耦合;
RPC和微服務纔算是同一級別的東西,即實現分佈式可使用rpc也可使用微服務;
SOA是解決海量併發訪問的終極解決方案,不管是採用RPC仍是微服務
引用官方原話:
在大規模服務化以前,應用可能只是經過 RMI 或 Hessian 等工具,簡單的暴露和引用遠程服務,經過配置服務的URL地址進行調用,經過 F5 等硬件進行負載均衡。
當服務愈來愈多時,服務 URL 配置管理變得很是困難,F5 硬件負載均衡器的單點壓力也愈來愈大。此時須要一個服務註冊中心,動態地註冊和發現服務,使服務的位置透明。並經過在消費方獲取服務提供方地址列表,實現軟負載均衡和 Failover,下降對 F5 硬件負載均衡器的依賴,也能減小部分紅本。
當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪一個應用要在哪一個應用以前啓動,架構師都不能完整的描述應用的架構關係。 這時,須要自動畫出應用間的依賴關係圖,以幫助架構師理清關係。
接着,服務的調用量愈來愈大,服務的容量問題就暴露出來,這個服務須要多少機器支撐?何時該加機器? 爲了解決這些問題,第一步,要將服務如今天天的調用量,響應時間,都統計出來,做爲容量規劃的參考指標。其次,要能夠動態調整權重,在線上,將某臺機器的權重一直加大,並在加大的過程當中記錄響應時間的變化,直到響應時間到達閾值,記錄此時的訪問量,再以此訪問量乘以機器數反推總容量。
簡單的說,Dubbo不只僅是實現了RPC,同時提供了整套分佈式服務的管理方案; 包括
服務註冊與發現
提供可視化的服務治理工具,和運維工具
舉例
角色:
節點 | 角色說明 |
---|---|
Provider |
暴露服務的服務提供方 |
Consumer |
調用遠程服務的服務消費方 |
Registry |
服務註冊與發現的註冊中心 |
Monitor |
統計服務的調用次數和調用時間的監控中心 |
Container |
服務運行容器 |
調用過程:
1.建立鋪Maven工程DubboDemo
2.在當前工程下建立provider模塊
3.爲provider添加依賴
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <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.13</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.11</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>
4.dubbo發佈服務的單位是接口,因此咱們須要建立一個服務接口,在消費端也須要一樣的接口來產生代理對象,爲了抽取公共部分代碼,能夠新建一個模塊而後讓提供方和消費方依賴這個項目從而找到須要的接口
在公共模塊中建立接口:
package com.yyh.service; public interface HelloService { String helloMan(String name); }
在pom中和依賴剛纔新建的項目
<dependency> <groupId>org.example</groupId> <artifactId>hello_Interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
5.在provider中建立實現類
package com.yyh.service.impl; import com.yyh.service.HelloService; public class HelloServiceImpl implements HelloService { public String helloMan(String name) { return "hello: "+name; } }
6.編寫提供方配置文件
<?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="hello-service"> <dubbo:parameter key="qos.enable" value="true"/> </dubbo:application> <!--dubbo這個服務所要暴露的服務地址所對應的註冊中心--> <dubbo:registry address="N/A"/> <!--當前服務發佈所依賴的協議;webserovice、Thrift、Hessain、http--> <dubbo:protocol name="dubbo" port="20880"/> <!--服務發佈的配置,須要暴露的服務接口--> <dubbo:service interface="com.yyh.service.HelloService" ref="helloService"/> <!--Bean bean定義--> <bean id="helloService" class="com.yyh.service.impl.HelloServiceImpl"/> </beans>
7.啓動服務
import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Runner { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:provider.xml"); context.start(); System.out.println("send anyket to exit"); System.in.read(); } }
8.爲了方便調試咱們能夠提供一個日誌配置在資源目錄下名爲log4j.properties
log4j.rootLogger=info,console 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 = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r]-[%p] %m%n
9.建立消費端模塊
在pom中引入一樣的依賴
10.建立配置文件consumer.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="hello-consumer"/> <!--註冊中心 --> <dubbo:registry address="N/A"/> <!--建立一個代理對象到容器中 --> <dubbo:reference id="helloService" interface="com.yyh.service.HelloService" url="dubbo://192.168.2.5:20880/com.yyh.service.HelloService"/> </beans>
11.運行測試:
import com.yyh.service.HelloService; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Runner { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml"); HelloService helloService = (HelloService) context.getBean("helloService"); String jerry = helloService.helloMan("jerry"); System.out.println(jerry); } }
若輸出hello jerry
則表示調用服務成功了;