Dubbo 入門-細說分散式與叢集
阿新 • • 發佈:2020-03-08
# 什麼是Dubbo
Dubbo是一款高效能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。
# 什麼是RPC
RPC全稱(Remote Procedure Call)遠端過程呼叫
過程指的是某個程式碼片段的執行,遠端呼叫則意味著我們可以在其他程序,甚至其他機器上去呼叫這段程式碼,當然也能獲取到其執行後的返回值,按照這個定義,我們請求某個http地址得到相應資料其實也算一次RPC,但是這樣的方式太過麻煩,(資料要先打包成http請求格式,在呼叫相關的請求庫,拿到的結果也是文字格式的需要在進行轉換),執行效率,和開發效率相比RPC則低一些;
我們需要一種更簡單的方式來完成分散式開發中的RPC環節,這也是Dubbo的核心所在,有多簡單呢? 呼叫遠端伺服器上的某個服務時就像是呼叫本地的某個方法一樣簡單,就像下面這樣
![](https://img2020.cnblogs.com/blog/1440878/202003/1440878-20200307225523566-313007401.png)
## 為什麼需要rpc
RPC是用來實現分散式構架的基石,分散式構架將同一個系統中的不同模組拆分到不同的子系統中,而子系統又分佈在不同的伺服器上,這時就需要RPC在來完成子系統之間的相互訪問;
可以這麼說分散式少不了RPC,RPC也要在分散式系統中才能發揮其核心價值;
## rpc的實現原理
毫無以為底層肯定是要通過socket來進行網路通訊的,但是如何能夠直接呼叫另一個機器上的方法呢?
![](https://img2020.cnblogs.com/blog/1440878/202003/1440878-20200307225543418-1115156364.png)
服務消費方(client)呼叫以本地呼叫方式呼叫服務;
2)client stub接收到呼叫後負責將方法、引數等組裝成能夠進行網路傳輸的訊息體;
3)client stub找到服務地址,並將訊息傳送到服務端;
4)server stub收到訊息後進行解碼;
5)server stub根據解碼結果呼叫本地的服務;
6)本地服務執行並將結果返回給server stub;
7)server stub將返回結果打包成訊息併發送至消費方;
8)client stub接收到訊息,並進行解碼;
9)服務消費方得到最終結果。
當然傳遞的引數或返回值是某個Java物件時則還需要對其進行序列化與反序列化
# 分散式與叢集
## 叢集:
叢集構架是將相同的處理邏輯進行復制(複製一份原始碼),創建出一組具備相同功能的服務集合,叢集中每個服務都能夠獨立的完成使用者的請求,它們之間基本上不需要互相通訊,也就用不上RPC了;
## 分散式:
分散式指的是將一個系統拆分為多個獨立的子系統,部署在不同的機器上;
在處理任務時會將一個任務拆分成若干子任務,分發給不同的子系統處理,每個子系統僅能處理一部分任務,通常一個完整的任務包含多個處理步驟,例如使用者要購買某個商品,需要先建立訂單,然後修改庫存,假設修改庫存的服務由另一個伺服器提供這時候RPC就閃亮登場了;
可以發現分散式與叢集在底層構架上完全不同,所以要將一個原本叢集的系統重構為分散式的話,則需要大量的修改,所以若系統後期存在高併發的需求,則可以在專案初期就採用分散式構架來搭建;
分散式是必要的嗎?
![](https://img2020.cnblogs.com/blog/1440878/202003/1440878-20200307225605193-1844182477.png)
![](https://img2020.cnblogs.com/blog/1440878/202003/1440878-20200307225628712-2115645774.png)
## 分散式的優缺點:
- 可將原本序列的任務變為並行執行(沒有前後依賴),提高計算速度
- 提高可用性,由於系統分佈在不同的計算節點上,其中某個節點失效不會對整個系統產生太大的影響
- 各個子系統獨立執行,極大的降低了系統的耦合度,使得各個子系統的擴充套件性和可業務功能的維護性提高
- 因為模組化,所以系統模組重用度更高(系統級別)
- 技術開放,多樣化,完全可以使用其他語言,其他平臺來開發某個子系統
- 更有效的利用硬體資源
缺點:
- 因為需要走RPC,響應時間變長
- 系統構架更加複雜,運維工作麻煩
- 需要進行服務管理和排程
- 測試和除錯更復雜
- 公共模組無法複用(程式碼級別)
需要強調的是:分散式和叢集並不是只能二選一,在高併發下場景下還可以給壓力大的節點組建叢集;\
分散式與微服務:
分散式系統是多個處理機通過通訊線路互聯而構成的鬆散耦合的系統,是一個更寬泛的概念;
微服務從結構上來看也屬於分散式,微服務強調的是將某個功能完完全全的獨立出來,徹底的解開耦合;
RPC和微服務才算是同一級別的東西,即實現分散式可以使用rpc也可以使用微服務;
###### 系統構架演進:
![image](http://dubbo.apache.org/docs/zh-cn/user/sources/images/dubbo-architecture-roadmap.jpg)
SOA是解決海量併發訪問的終極解決方案,無論是採用RPC還是微服務
# 為什麼需要Dubbo:
引用官方原話:
在大規模服務化之前,應用可能只是通過 RMI 或 Hessian 等工具,簡單的暴露和引用遠端服務,通過配置服務的URL地址進行呼叫,通過 F5 等硬體進行負載均衡。
**當服務越來越多時,服務 URL 配置管理變得非常困難,F5 硬體負載均衡器的單點壓力也越來越大。**此時需要一個服務註冊中心,動態地註冊和發現服務,使服務的位置透明。並通過在消費方獲取服務提供方地址列表,實現軟負載均衡和 Failover,降低對 F5 硬體負載均衡器的依賴,也能減少部分成本。
**當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪個應用要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關係。** 這時,需要自動畫出應用間的依賴關係圖,以幫助架構師理清關係。
**接著,服務的呼叫量越來越大,服務的容量問題就暴露出來,這個服務需要多少機器支撐?什麼時候該加機器?** 為了解決這些問題,第一步,要將服務現在每天的呼叫量,響應時間,都統計出來,作為容量規劃的參考指標。其次,要可以動態調整權重,在線上,將某臺機器的權重一直加大,並在加大的過程中記錄響應時間的變化,直到響應時間到達閾值,記錄此時的訪問量,再以此訪問量乘以機器數反推總容量。
簡單的說,Dubbo不僅僅是實現了RPC,同時提供了整套分散式服務的管理方案; 包括
- 服務註冊與發現
- 負載均衡
- 流量排程
- 提供視覺化的服務治理工具,和運維工具
# 構架及服務呼叫流程
![dubbo-architucture](http://dubbo.apache.org/docs/zh-cn/user/sources/images/dubbo-architecture.jpg)
舉例
![](https://img2020.cnblogs.com/blog/1440878/202003/1440878-20200307225657511-1101870744.png)
角色:
| 節點 | 角色說明 |
| ----------- | -------------------------------------- |
| `Provider` | 暴露服務的服務提供方 |
| `Consumer` | 呼叫遠端服務的服務消費方 |
| `Registry` | 服務註冊與發現的註冊中心 |
| `Monitor` | 統計服務的呼叫次數和呼叫時間的監控中心 |
| `Container` | 服務執行容器 |
呼叫過程:
1. 服務容器負責啟動,載入,執行服務提供者。
2. 服務提供者在啟動時,向註冊中心註冊自己提供的服務。
3. 服務消費者在啟動時,向註冊中心訂閱自己所需的服務。
4. 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連線推送變更資料給消費者。
5. 服務消費者,從提供者地址列表中,基於軟負載均衡演算法,選一臺提供者進行呼叫,如果呼叫失敗,再選另一臺呼叫。
6. 服務消費者和提供者,在記憶體中累計呼叫次數和呼叫時間,定時每分鐘傳送一次統計資料到監控中心。
# hello Dubbo
1.建立鋪Maven工程DubboDemo
2.在當前工程下建立provider模組
3.為provider新增依賴
```xml
junit
junit
4.12
test
com.alibaba
dubbo
2.6.6
org.apache.zookeeper
zookeeper
3.4.13
com.101tec
zkclient
0.11
io.netty
netty-all
4.1.32.Final
org.apache.curator
curator-framework
2.8.0
```
4.dubbo釋出服務的單位是介面,所以我們需要建立一個服務介面,在消費端也需要同樣的介面來產生代理物件,為了抽取公共部分程式碼,可以新建一個模組然後讓提供方和消費方依賴這個專案從而找到需要的介面
在公共模組中建立介面:
```java
package com.yyh.service;
public interface HelloService {
String helloMan(String name);
}
```
在pom中和依賴剛才新建的專案
```xml
org.example
hello_Interface
1.0-SNAPSHOT
```
5.在provider中建立實現類
```java
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
```
7.啟動服務
```java
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`
```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
```
11.執行測試:
```java
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`則表示呼叫服務成功了;