Dubbo 延遲與粘滯鏈接

前言

你們好,今天開始給你們分享 — Dubbo 專題之 Dubbo 延遲和粘滯鏈接。在前一個章節中咱們介紹了 Dubbo 併發控制,Dubbo 爲咱們提供兩大類的配置:消費端的配置和服務提供端配置,咱們分別能夠對服務提供端和服務消費端進行併發數量的控制。同時咱們也例舉了常見的使用場景而且進行了源碼解析來分析其實現原理。有的小夥伴學習了併發控制可能會想到:若是咱們的服務消費端有大量的服務須要引用,那咱們的 Dubbo 應用程序可能啓動至關的緩慢其緣由是:當咱們消費端應用啓動的時候須要獲取遠程服務的代理對象的引用,若是咱們每個獲取的遠程代理對象都在啓動的時候建立鏈接,這樣一定會影響咱們的應用程序啓動,幸虧咱們的 Dubbo 提供一種配置的方式解決這個問題。那同時也延伸出另一個問題,就是咱們對某個服務的調用能不能一直分配到上次調用的服務提供者呢?帶着這些疑問咱們開始本章節學習,咱們會經過介紹什麼是延遲和粘滯鏈接?怎樣經過參數配置改變默認行爲?來解決這些問題。下面就讓咱們快速開始吧!java

1. 延遲和粘滯鏈接簡介

經過前面對 Dubbo 相關章節的介紹我相信你們應該有個基本的概念就是咱們 Dubbo 中服務消費方持有服務提供端的服務引用,這個引用又經過層層的代理最終經過咱們的 TCP/IP (底層使用Netty進行網絡通信)與遠程服務端進行通信。在 Dubbo 中爲了優化消費端獲取服務提供端的引用對象時候建立底層的物理鏈接,好比在與 Spring 集成中咱們的引用對象須要經過 Spring 容器進行對外發布 Bean 實例,然而此時咱們並無真正的使用代理對象,只是將代理對象交給 Spring 管理。爲了使代理對象在真正使用的時候纔去建立底層的物理從而減小底層鏈接的建立和釋放這就叫作延遲鏈接。同理粘滯鏈接的意思就是儘量讓客戶端老是向同一提供者發起調用,除非該提供者掛了,再連另外一臺。下圖簡單的描述了粘滯鏈接在第二次、第三次調用服務的時候調用原服務提供者:git

粘滯鏈接

2. 配置方式

下面咱們主要經過 XML 和 註解的方式進行配置介紹:spring

2.1 延遲鏈接:

  1. XML 方式
<dubbo:reference id="bookFacade"
                     interface="com.muke.dubbocourse.common.api.BookFacade" lazy="true" ></dubbo:reference>
  1. 註解方式
@Reference(lazy = true)

2.2 粘滯鏈接

  1. XML 方式
<dubbo:reference id="bookFacade" interface="com.muke.dubbocourse.common.api.BookFacade" sticky="true" />

方法級別控制:apache

<dubbo:reference id="xxxService" interface="com.muke.dubbocourse.common.api.BookFacade">
    <dubbo:mothod name="queryAll" sticky="true" />
</dubbo:reference>
  1. 註解方式
@Reference(sticky = true)

從上面的配置中咱們能夠簡單的總結:延遲鏈接經過lazy進行配置,粘滯鏈接使用sticky進行配置。編程

3. 使用場景

根據前面的介紹咱們大概理解了什麼是延遲和粘滯鏈接。其中延遲鏈接就是爲了減小無用的鏈接而在真正使用對象時候才建立鏈接,而粘滯鏈接是爲了屢次調用都儘量地使用同一個服務提供者也有減小服務鏈接建立的做用。下面咱們簡單的介紹幾種常見使用場景:api

  1. 當咱們的服務消費端須要大量的引用服務提供者或者建立遠程鏈接成本很是高(通常指耗時時間)時咱們能夠考慮開啓延遲鏈接。
  2. 假設咱們的應用有大量的靜態數據須要加載到應用本地緩存( JVM 緩存)時當第一次調用Service A進行緩存加載,那麼在第二次調用的時候咱們指望也調用剛纔已經存在緩存的服務 Service A 這樣提升了服務的訪問速度。這種場景可使用粘滯鏈接。
  3. 若是咱們的應用調用過程存在某種狀態,例如:調用服務 Service A 進行用戶登陸返回 token,那第二次調用查詢用戶信息的時候須要根據攜帶的token來查詢用戶的登陸狀態,此時若是訪問 Service A 那麼用戶登陸的 token 信息是存的,若是訪問到 Service B 這時就不存在 token (假設這裏沒有使用分佈式緩存)。這種場景可使用粘滯鏈接。

4. 示例演示

下面我以獲取圖書列表爲例進行演示。項目結構以下:緩存

idea

咱們的延遲鏈接主要配置在服務消費端dubbo-consumer-xml.xml配置文件以下:服務器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       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="demo-consumer" logger="log4j"/>

    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <!--lazy="true"延遲鏈接-->
    <dubbo:reference id="bookFacade"
                     interface="com.muke.dubbocourse.common.api.BookFacade" lazy="true" ></dubbo:reference>

</beans>

經過lazy="true"配置消費端引用服務提供者服務時開啓延遲鏈接。下面咱們繼續看看粘滯鏈接配置:微信

<!--sticky="true"開啓粘滯鏈接 -->
    <dubbo:reference id="bookFacade"
                     interface="com.muke.dubbocourse.common.api.BookFacade" sticky="true" ></dubbo:reference>

經過sticky="true"配置消費端引用服務提供者服務時開啓粘滯鏈接。網絡

Tips:這裏在演示粘滯鏈接的時候小夥伴們在部署應用的時候至少須要部署兩個或以上的實例才能看出效果。若是咱們開啓粘滯鏈接那麼咱們能夠看到老是訪問同一個服務提供者。

5. 實現原理

下面咱們經過源碼的方式簡單的分析它們的實現原理。

首先是延遲鏈接其核心方法`org.apache.dubbo.rpc.protocol.dubbo.
DubboProtocol#protocolBindingRefer`代碼以下:

/***
     *
     * 協議綁定並建立鏈接
     *
     * @author liyong
     * @date 16:11 2020-03-08
     * @param serviceType
     * @param url
     * @exception
     * @return org.apache.dubbo.rpc.Invoker<T>
     **/
    @Override
    public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);

        // 建立DubboInvoker而且建立網絡鏈接
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

這裏的 DubboInvoker 是咱們遠程 RPC 調用的封裝,這裏`org.apache.dubbo.rpc.protocol.dubbo.
DubboProtocol#getClients`方法建立客戶端鏈接而且綁定請求處理器核心代碼以下:

/***
     *
     * 獲取客戶端鏈接,而且綁定了請求處理器
     *
     * @author liyong
     * @date 16:55 2020-03-08
     * @param url
     * @exception
     * @return org.apache.dubbo.remoting.exchange.ExchangeClient[]
     **/
    private ExchangeClient[] getClients(URL url) {
        //...
        ExchangeClient[] clients = new ExchangeClient[connections];
        for (int i = 0; i < clients.length; i++) {
            if (useShareConnect) {
                clients[i] = shareClients.get(i);//共享鏈接

            } else {
                clients[i] = initClient(url);//不共享 新建鏈接
            }
        }

        return clients;
    }

其中initClient方法建立鏈接核心代碼以下:

private ExchangeClient initClient(URL url) {

        //...

        ExchangeClient client;
        try {
            //是否延遲鏈接 lazy="true"
            if (url.getParameter(LAZY_CONNECT_KEY, false)) {
                //這裏並無建立鏈接對象
                client = new LazyConnectExchangeClient(url, requestHandler);

            } else {
                //HeaderExchangeClient鏈接到服務器並綁定請求處理器
                client = Exchangers.connect(url, requestHandler);
            }

        } catch (RemotingException e) {
           //...
        }

        return client;
    }

根據咱們的配置獲取LAZY_CONNECT_KEY參數(lazy)的值,當咱們配置爲true時建立LazyConnectExchangeClient對象,爲false是建立物理鏈接。

下面繼續討論粘滯鏈接核心方法`org.apache.dubbo.rpc.cluster.support.
AbstractClusterInvoker#select`以下:

protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation,
                                List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {

        if (CollectionUtils.isEmpty(invokers)) {
            return null;
        }
        String methodName = invocation == null ? StringUtils.EMPTY_STRING : invocation.getMethodName();

        //獲取粘滯鏈接配置
        boolean sticky = invokers.get(0).getUrl()
                .getMethodParameter(methodName, CLUSTER_STICKY_KEY, DEFAULT_CLUSTER_STICKY);

        //ignore overloaded method
        if (stickyInvoker != null && !invokers.contains(stickyInvoker)) {
            stickyInvoker = null;
        }
        //開啓粘滯鏈接配置 且存在已經建立的stickyInvoker粘滯鏈接
        if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))) {
            //有效性檢測
            if (availablecheck && stickyInvoker.isAvailable()) {
                return stickyInvoker;
            }
        }

        //根據負載均衡策略進行服務選擇
        Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected);

        if (sticky) {
            //若是配置粘滯鏈接爲true 則當前選擇的Invoker保存在stickyInvoker粘滯鏈接變量
            stickyInvoker = invoker;
        }
        return invoker;
    }

從上面的代碼咱們能夠看出從 URL 中獲取 sticky 配置判斷是否開啓粘滯鏈接,若是開啓那麼在第一次獲取 InvokerstickyInvokernull 建立一個 Invoker 代理對象,當第二次獲取 Invoker 時會判斷是否存在 stickyInvoker 若是存在且沒有被排除則繼續使用前面保存下來的 Invoker 代理對象也就是stickyInvoker

Tips:這裏 URL 包括咱們配置的 XML 或註解配置中的參數、通信協議、序列化方式等等參數,能夠參考前面章節詳細描述。

6. 小結

在本小節中咱們主要學習了 Dubbo 延遲和粘滯鏈接,同時咱們也分析了延遲和粘滯鏈接的實現原理。延遲鏈接本質上是延遲 Dubbo 底層物理鏈接的建立,而粘滯鏈接本質上是重複利用已建立的鏈接。

本節課程的重點以下:

  1. 理解 Dubbo 延遲和粘滯鏈接
  2. 瞭解了延遲和粘滯鏈接使用方式
  3. 瞭解延延遲和粘滯鏈接使用場景
  4. 瞭解延遲和粘滯鏈接實現原理

做者

我的從事金融行業,就任過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就任於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公衆號和博客站點對知識體系進行分享。關注公衆號: 青年IT男 獲取最新技術文章推送!

博客地址: http://youngitman.tech

微信公衆號:

相關文章
相關標籤/搜索