Dubbo 入門-細說分佈式與集群

什麼是Dubbo

Dubbo是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調用,智能容錯和負載均衡,以及服務自動註冊和發現。

什麼是RPC

RPC全稱(Remote Procedure Call)遠程過程調用

過程指的是某個代碼片段的執行,遠程調用則意味著我們可以在其他進程,甚至其他機器上去調用這段代碼,當然也能獲取到其執行後的返回值,按照這個定義,我們請求某個http地址得到相應數據其實也算一次RPC,但是這樣的方式太過麻煩,(數據要先打包成http請求格式,在調用相關的請求庫,拿到的結果也是文本格式的需要在進行轉換),執行效率,和開發效率相比RPC則低一些;

我們需要一種更簡單的方式來完成分佈式開發中的RPC環節,這也是Dubbo的核心所在,有多簡單呢? 調用遠程服務器上的某個服務時就像是調用本地的某個方法一樣簡單,就像下面這樣

Dubbo 入門-細說分佈式與集群

為什麼需要rpc

RPC是用來實現分佈式構架的基石,分佈式構架將同一個系統中的不同模塊拆分到不同的子系統中,而子系統又分佈在不同的服務器上,這時就需要RPC在來完成子系統之間的相互訪問;

可以這麼說分佈式少不了RPC,RPC也要在分佈式系統中才能發揮其核心價值;

rpc的實現原理

毫無以為底層肯定是要通過socket來進行網絡通訊的,但是如何能夠直接調用另一個機器上的方法呢?

Dubbo 入門-細說分佈式與集群

服務消費方(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就閃亮登場了;

可以發現分佈式與集群在底層構架上完全不同,所以要將一個原本集群的系統重構為分佈式的話,則需要大量的修改,所以若系統後期存在高併發的需求,則可以在項目初期就採用分佈式構架來搭建;

分佈式是必要的嗎?

Dubbo 入門-細說分佈式與集群

Dubbo 入門-細說分佈式與集群

分佈式的優缺點:

  • 可將原本串行的任務變為並行執行(沒有前後依賴),提高計算速度
  • 提高可用性,由於系統分佈在不同的計算節點上,其中某個節點失效不會對整個系統產生太大的影響
  • 各個子系統獨立運行,極大的降低了系統的耦合度,使得各個子系統的擴展性和可業務功能的維護性提高
  • 因為模塊化,所以系統模塊重用度更高(系統級別)
  • 技術開放,多樣化,完全可以使用其他語言,其他平臺來開發某個子系統
  • 更有效的利用硬件資源

缺點:

  • 因為需要走RPC,響應時間變長
  • 系統構架更加複雜,運維工作麻煩
  • 需要進行服務管理和調度
  • 測試和調試更復雜
  • 公共模塊無法複用(代碼級別)

需要強調的是:分佈式和集群並不是只能二選一,在高併發下場景下還可以給壓力大的節點組建集群;

分佈式與微服務:

分佈式系統是多個處理機通過通信線路互聯而構成的鬆散耦合的系統,是一個更寬泛的概念;

微服務從結構上來看也屬於分佈式,微服務強調的是將某個功能完完全全的獨立出來,徹底的解開耦合;

RPC和微服務才算是同一級別的東西,即實現分佈式可以使用rpc也可以使用微服務;

系統構架演進:

Dubbo 入門-細說分佈式與集群

SOA是解決海量併發訪問的終極解決方案,無論是採用RPC還是微服務

為什麼需要Dubbo:

引用官方原話:

在大規模服務化之前,應用可能只是通過 RMI 或 Hessian 等工具,簡單的暴露和引用遠程服務,通過配置服務的URL地址進行調用,通過 F5 等硬件進行負載均衡。

當服務越來越多時,服務 URL 配置管理變得非常困難,F5 硬件負載均衡器的單點壓力也越來越大。此時需要一個服務註冊中心,動態地註冊和發現服務,使服務的位置透明。並通過在消費方獲取服務提供方地址列表,實現軟負載均衡和 Failover,降低對 F5 硬件負載均衡器的依賴,也能減少部分成本。

當進一步發展,服務間依賴關係變得錯蹤複雜,甚至分不清哪個應用要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關係。這時,需要自動畫出應用間的依賴關係圖,以幫助架構師理清關係。

接著,服務的調用量越來越大,服務的容量問題就暴露出來,這個服務需要多少機器支撐?什麼時候該加機器?為了解決這些問題,第一步,要將服務現在每天的調用量,響應時間,都統計出來,作為容量規劃的參考指標。其次,要可以動態調整權重,在線上,將某臺機器的權重一直加大,並在加大的過程中記錄響應時間的變化,直到響應時間到達閾值,記錄此時的訪問量,再以此訪問量乘以機器數反推總容量。

簡單的說,Dubbo不僅僅是實現了RPC,同時提供了整套分佈式服務的管理方案; 包括

  • 服務註冊與發現
  • 負載均衡
  • 流量調度
  • 提供可視化的服務治理工具,和運維工具

構架及服務調用流程

Dubbo 入門-細說分佈式與集群

舉例

Dubbo 入門-細說分佈式與集群

角色:

節點角色說明Provider暴露服務的服務提供方Consumer調用遠程服務的服務消費方Registry服務註冊與發現的註冊中心Monitor統計服務的調用次數和調用時間的監控中心Container服務運行容器

調用過程:

  1. 服務容器負責啟動,加載,運行服務提供者。
  2. 服務提供者在啟動時,向註冊中心註冊自己提供的服務。
  3. 服務消費者在啟動時,向註冊中心訂閱自己所需的服務。
  4. 註冊中心返回服務提供者地址列表給消費者,如果有變更,註冊中心將基於長連接推送變更數據給消費者。
  5. 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,如果調用失敗,再選另一臺調用。
  6. 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。

hello Dubbo

1.創建鋪Maven工程DubboDemo

2.在當前工程下創建provider模塊

3.為provider添加依賴

<code><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>/<code>

4.dubbo發佈服務的單位是接口,所以我們需要創建一個服務接口,在消費端也需要同樣的接口來產生代理對象,為了抽取公共部分代碼,可以新建一個模塊然後讓提供方和消費方依賴這個項目從而找到需要的接口

在公共模塊中創建接口:

<code>package com.yyh.service;

public interface HelloService {
String helloMan(String name);
}/<code>

​ 在pom中和依賴剛才新建的項目

<code><dependency>
<groupid>org.example/<groupid>
<artifactid>hello_Interface/<artifactid>
<version>1.0-SNAPSHOT/<version>
/<dependency>/<code>

5.在provider中創建實現類

<code>package com.yyh.service.impl; 


import com.yyh.service.HelloService;

public class HelloServiceImpl implements HelloService {
public String helloMan(String name) {
return "hello: "+name;
}
}/<code>

6.編寫提供方配置文件

<code>
<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">

<application>
<parameter>
/<application>

<registry>

<protocol>

<service>

<bean>
/<beans>/<code>

7.啟動服務

<code>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();
}
}/<code>

8.為了方便調試我們可以提供一個日誌配置在資源目錄下名為 log4j.properties

<code>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/<code>

9.創建消費端模塊

在pom中引入同樣的依賴

10.創建配置文件consumer.xml

<code>
<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">

<application>

<registry>

<reference> url="dubbo://192.168.2.5:20880/com.yyh.service.HelloService"/>

/<reference>/<beans>/<code>

11.運行測試:

<code>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);
}
}/<code>

若輸出 hello jerry 則表示調用服務成功了;


分享到:


相關文章: