dubbo的使用和基本的功能用法

1. dubbo 有什麼用

在分佈式系統中,服務與服務之間怎麼通信是一個問題,目前主流的方式就是通過RPC或HTTP協議進行通信。像Spring Cloud就是通過http協議進行服務之間的通信,而dubbo是一個RPC框架,它實現了RPC調用。這兩種方式對比起來的話,HTTP協議稍微簡單點,但是由於它需要3次握手和4次揮手,性能較差,而dubbo實現的RPC,底層是用netty這種非阻塞I/O,速度會快很多。

性能:

  • http: 由於它是無狀態的,每次調用它都需要3次握手和4次揮手,性能較差。
  • dubbo: 底層是用netty使種非阻塞I/O,性能較好。

編程過程中的使用方式:

  • http: 兩個系統的代碼沒有任何交集,只需要一個提供服務,一個調用服務,通過json串來傳輸數據
  • dubbo:由於RPC的調用就像調用本地方法一樣調用遠程方法。那在分佈式系統中,各個系統的文件都是獨享的,那我怎麼知道你把我需要的服務寫在哪個方法了?此時,就需要一箇中間媒介,這個媒介就是存放接口的一個項目,該項目中只放一些公共的接口和domain,然後服務提供方和服務消費方都去依賴這個項目,並且服務提供方去實現方法,並把方法暴露出來,服務消費方,就可以引入接口,然後調用方法。由此來完成服務的調用

那除了解決分佈式系統之間的通信問題,dubbo還提供了一些其他的功能,:

服務自動註冊與發現,服務熔斷,服務降級,軟負載均衡等,功能還在持續更新

由於Spring Cloud 是一個分佈式一站式的解決方案,且代碼都是通過上層接口去實現各個組件,所以可以直接在spring cloud中使用dubbo作爲RPC通信框架,來解決Spring Cloud使用http協議的性能問題。

Spring Cloud Alibaba 集成了Alibaba的一些開源組件,使得Spring Cloud中的組件選擇更加豐富。

2.  dubbo架構

官網上有:具體說明官網上有

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

 3. 特點

連通性、健壯性、伸縮性、以及向未來架構的升級性。官方文檔都有,我自己歸納整理了一下:

連通性

  • 服務提供者和消費者只在啓動時與註冊中心交互,服務提供者註冊服務到註冊中心,服務消費者向註冊中心請求獲取服務清單
  • 之後的請求,消費者直接通過初始時獲取的清單,並使用負載均衡來決定訪問哪一臺服務器
  • 註冊中心,服務提供者,服務消費者三者之間均爲長連接,當註冊中心感知到有服務的上下線,那麼會立即推送事件通知消費者,然後消費者更新本地服務清單
  • 註冊中心和監控中心全部宕機,不影響已運行的提供者和消費者,消費者在本地緩存了提供者列表
  • 註冊中心和監控中心都是可選的,當不使用註冊中心,服務消費者可以直連服務提供者,當然這樣寫代碼就比較麻煩了

健壯性

  • zk的數據庫宕掉後,註冊中心仍能通過緩存提供服務列表查詢,但不能註冊新服務
  • 註冊中心在集羣模式下,任意一臺宕掉後,都不影響使用,但是宕機的機器不能超過集羣總數的一半以上,否則zk就無法使用了。且如果宕掉的是leader,那麼需要重新選擇leader,該過程比較耗時,且該過程中,服務無法使用
  • 註冊中心全部宕掉後,服務提供者和服務消費者仍能通過本地緩存通訊
  • 服務提供者宕機對zk沒有任何影響,只是需要通知一下消費者,如果服務提供者全部宕機,服務消費者應用將無法使用,並無限次重連等待服務提供者恢復

伸縮性

  • 註冊中心爲對等集羣,可動態增加機器部署實例,所有客戶端將自動發現新的註冊中心
  • 服務提供者無狀態,可動態增加機器部署實例,註冊中心將推送新的服務提供者信息給消費者

 4. 使用springboot 集成dubbo

集成dubbo的方式有很多種,我們這裏就用springboot來集成。

需要創建3個項目,

  • gmall-interface:提供公共接口和公共pojo
  • gmall-user: 服務提供者,提供一個查詢用戶列表的服務
  • gmall-order:服務消費者,消費用戶服務

dubbo-spring-boot-starter 依賴有兩個,一個是:

另一個是

 看更新時間就能知道,apache.dubbo是最新的,且之後的dubbo-spring-boot-starter都是交給apache的。我們這裏就使用apache.dubbo。

dubbo: https://github.com/apache/dubbo

dubbo-spring-boot地址:https://github.com/apache/dubbo-spring-boot-project

我的spring boot 版本是2.2.6.RELEASE,

dubbo-spring-boot-starter的版本是2.7.3

4.1 gmall-interface

該項目只存放公共的資源,所以不需要使用spring boot,創建一個普通的maven工程即可

結構如下:

UserService:

public interface UserService {
    List<User> getUserList();
}

4.2 gmall-user

創建一個springboot項目

引入依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
<!--引入公共模塊-->
    <dependency>
        <groupId>com.tanfp.dubbo-study.dubbo-demo</groupId>
        <artifactId>gmall-interface</artifactId>
        <version>1.0.0</version>
    </dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.3</version>
</dependency>
 <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.48.Final</version>
        </dependency>
</dependencies>

必須要引入curator-framework,curator-recipes這兩個包,當你使用zookeeper作爲註冊中心時,它就會去者兩個包中找一些類來操作zookeeper。

netty-all這個包當你遇到io/netty的問題再加吧,我這裏其實不加也是可以正常訪問的

結構如下,裏面放gmall-interface項目中接口的實現類

UserserviceImpl.class

// 注意引入包時,引入dubbo的Service包
@Service(version = "1.0")
public class UserServiceImpl implements UserService {
    @Override
    public List<User> getUserList() {
        List<User> userList = new ArrayList<>();
        User user = new User("張三z",1,new Date());
        User user2 = new User("李四",2,new Date());
        userList.add(user);
        userList.add(user2);
        return userList;
    }
}

@Service表示對外暴露了這個類,即暴露了服務,可以讓消費者來直接在本地調用UserserviceImpl類中的方法

添加註解

啓動類上添加@EnableDubbo註解

修改application.yml

server.port=8082
#指定當前dubbo引用的名稱
dubbo.application.name=gmall-user
#指定使用什麼註冊中心
dubbo.registry.protocol=zookeeper
#指定註冊中心的地址
dubbo.registry.address=127.0.0.1:2181
#指定使用什麼通信協議
dubbo.protocol.name=dubbo

4.3 gmall-order

創建一個spring boot項目,依賴與gmall-user項目一樣

結構如下

 OrderController:

@RestController
public class OrderController {

    @Autowired
    private OrderServiceImpl orderService;

    @GetMapping("/users")
    public List<User> getUserList() {
        return orderService.getUserList();
    }
}

OrderServiceImpl:

@Service
public class OrderServiceImpl {

    // 調用dubbo服務
    @Reference(version = "1.0")
    private UserService userService;

    public List<User> getUserList() {
        return userService.getUserList();
    }
}

啓動類添加註解

啓動類添加@EnableDubbo註解

修改application.yml配置

server.port=8081
dubbo.application.name=gmall-order-web
dubbo.registry.protocol=zookeeper
dubbo.registry.address=127.0.0.1:2181
dubbo.protocol.name=dubbo

4.4 啓動訪問

啓動zookeeper集羣,然後分別啓動gmall-user(服務提供者),gmall-order(服務消費者)

訪問http://localhost:8081/users

5. 具體配置

dubbo中具體的配置項,在官網中都有

除了dubbo:service和dubbo:reference,其他的基本上都有,這兩個分別使用@Service,@Reference去配置

對應application.yml中就是:

dubbo.protocol.xxx=yyy
dubbo.registry.xxx=yyy

xxx表示具體的屬性,yyy表示值

6. 主要功能

6.1 服務降級

當服務器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務器資源以保證核心交易正常運作或高效運作。

可以通過服務降級功能臨時屏蔽某個出錯的非關鍵服務,並定義降級後的返回策略。

一般都是在消費者的reference屬性上添加mock值來判斷,也只能消費方纔能加,提供方加這個mock會報錯。其實想想也能知道,它有一種是不進行調用,直接返回錯誤,如果放在提供服務方,還怎麼直接返回?

mock的寫法有以下幾種:

  • return empty: 代表空,基本類型的默認值,或者集合類的空值
  • return null
  • return true
  • return false
  • return JSON 格式: 反序列化 JSON 所得到的對象
  • fail:return null   表示會進行遠程調用,如果調用失敗,則返回null
  • force:return null,直接不進行遠程調用了,方法直接返回null
  • throw    直接拋出異常對象
  • 自定義返回類
    @Reference(version = "1.0",mock = "fail:return null",timeout = 2000)
    @Reference(version = "1.0",mock = "force:return null",timeout = 2000)
    private UserService userService;

自定義返回:

自定義返回的類放在公共接口的項目:gmall-interface下,有兩點注意:

  1. 注意類的名稱是接口名+Mock
  2. 要放在與UserService接口相同的包下

 UserServiceMock:

public class UserServiceMock implements UserService {
    public List<User> getUserList() {
        System.out.println("調用了Mock");
        return new ArrayList<User>();
    }
}

消費方啓用mock功能:

 @Reference(version = "1.0",mock = "true",timeout = 2000)
 private UserService userService;

6.2 服務熔斷(集羣容錯)

在集羣調用失敗時,Dubbo 提供了多種容錯方案,缺省爲 failover 重試。

一般來說,服務提供方和服務消費方都能設置,但是能在服務提供方進行配置的就儘量在服務提供方設置,可以更好的管理服務

Failover Cluster

失敗自動切換,當出現失敗,重試其它服務器。通常用於讀操作,但重試會帶來更長延遲。可通過 retries="2" 來設置重試次數(不含第一次)。

在服務提供方添加重試次數。不設置cluster,則默認是Failover重試機制

@Service(version = "1.0",retries = 2, cluster="failsafe")

Failfast Cluster

快速失敗,只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。

非冪等性表示多次調用結果都是一樣的,比如查詢,刪除,更新

Failsafe Cluster

失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。

Failback Cluster

失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於消息通知操作。

Forking Cluster

並行調用多個服務器,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks="2" 來設置最大並行數。

Broadcast Cluster

廣播調用所有提供者,逐個調用,任意一臺報錯則報錯。通常用於通知所有提供者更新緩存或日誌等本地資源信息。

6.3 負載均衡

默認採用隨機算法

Random LoadBalance

  • 隨機,按權重設置隨機概率。
  • 在一個截面上碰撞的概率高,但調用量越大分佈越均勻,而且按概率使用權重後也比較均勻,有利於動態調整提供者權重。

RoundRobin LoadBalance

  • 輪詢,按公約後的權重設置輪詢比率。
  • 存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。

LeastActive LoadBalance

  • 最少活躍調用數,相同活躍數的隨機,活躍數指調用前後計數差。
  • 使慢的提供者收到更少請求,因爲越慢的提供者的調用前後計數差會越大。

ConsistentHash LoadBalance

  • 一致性 Hash,相同參數的請求總是發到同一提供者。

使用:

@Service(version = "1.0",retries = 2, cluster="failsafe", loadbalance="roundrobin")

6.4 服務分組

當一個接口有多種實現時,可以用 group 區分。

@Service(version = "1.0",retries = 2, group = "impl-1")
public class UserServiceImpl implements UserService {}

@Service(version = "1.0",retries = 2, group = "impl-2")
public class UserServiceImpl implements UserService {}

服務消費方引用的時候也加一個group屬性執行即可,也可以使用*:表示使用任意一個實現類

@Reference(version = "1.0",mock = "fail:return null",timeout = 2000,group = "impl-1")
// 使用任意組
@Reference(version = "1.0",mock = "fail:return null",timeout = 2000,group = "*")

6.5 多版本

當一個接口實現,出現不兼容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。

可以按照以下的步驟進行版本遷移:

  1. 在低壓力時間段,先升級一半提供者爲新版本
  2. 再將所有消費者升級爲新版本
  3. 然後將剩下的一半提供者升級爲新版本

使用方式就是指定version版本號,服務消費者只能消費指定版本的服務

@Service(version = "2.0.0")
@Reference(version = "2.0.0")

 

如果消費方不區分服務版本號,那麼消費方可以使用*

如果不需要區分版本,可以按照以下的方式配置 :

@Reference( version="*")

6.6 本地存根 

當服務消費方想在調用服務提供方前,去做一些事情,比如說註冊,服務消費方可以先執行一部分邏輯,比如參數的校驗,只有校驗成功,我們再去調用服務提供方的註冊方法,否則就直接返回錯誤。那這個具體怎麼實現呢?

分兩步:

  1. 先在gmall-interface項目的service包下創建一個UserServiceStub類,注意類名是接口名+Stub
  2. 開啓本地存根

創建一個UserServiceStub類

 一定要在與UserService接口同包的路徑下去創建UserServiceStub,否則會找不到這個類

public class UserServiceStub implements UserService {

    // 相當於是UserService的一個代理對象,當我們校驗完成後,可以通過這個代理對象調用服務
    private final UserService userService;

    public UserServiceStub(UserService userService) {
        this.userService = userService;
    }

    public List<User> getUserList() {
        System.out.println("校驗參數中......");
        if (true) {
            // 校驗成功,我們就繼續調用服務
           return userService.getUserList();
        } else {
            // 校驗失敗
            return new ArrayList<User>();
        }
    }
}

開啓本地存根

在這個版本中,如果你是以接口+Stub的這種形式創建的類,那麼當你創建完成後,本地存根其實就默認開啓了,但是最好還是顯示聲明一下,聲明即可在服務提供方聲明,也可以在服務消費方聲明,但是我們最好在提供方去聲明,統一管理服務。

設置stub爲true即可

@Service(version = "1.0",stub = "true")

6.7 本地僞裝

這個就是用服務降級來實現的,當服務提供方全部掛掉後,客戶端不拋出異常,而是通過 Mock 數據返回結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章