dubbo探究

一 佔位

待整理。。 html

二 問題彙總

1 談談dubbo的超時重試

dubbo 啓動時默認有重試機制和超時機制。若是在必定的時間內,provider沒有返回,則認爲本次調用失敗。重試機制出如今調用失敗時,會再次調用,若是在配置的調用次數內都失敗,則認爲這次請求異常,消費端出現RpcException提示retry了多少次仍是失敗。算法

若是出現超時,一般是業務處理太慢,可在服務提供方執行 jstack PID > jstack.log 分析線程都卡在哪一個方法調用上。若是不能調優性能,請將timeout設大。api

 

dubbo消費端設置的超時時間須要根據業務實際狀況來設定,若是設置的太短,一些複雜業務須要很長時間完成,致使在設定的超時時間內沒法完成正常的業務處理。這樣消費端達到超時時間,那麼dubbo會進行重試,不合理的重試在一些特殊的業務場景下可能會引起不少問題。好比發送郵件,可能會發出多份重複郵件等。服務器

 

dubbo調用服務不成功時,默認會重試兩次。dubbo的路由機制,會把超時的請求路由到其餘機器上,而不是本機嘗試,因此dubbo的重試機制也能獲得必定程度的保證。可是不合理地配置重試次數,當失敗時會進行重試屢次,這樣在某個時間點出現性能問題,調用方繼續重試請求爲正常retries倍,容易引發服務雪崩。網絡

最佳實踐:架構

1.對於核心的服務中心,去除dubbo超時重試機制,並從新評估設置超時時間。
2.業務處理代碼必須放在服務端,客戶端只作參數驗證和服務調用,不涉及業務流程處理併發

 

2 dubbo的provider和consumer都配置timeout超時時間, 以哪一個爲準

在dubbo的provider和consumer的配置文件中,若是都配置了timeout的超時時間,dubbo默認以consumer中配置的時間爲準dom

provider.xml的配置:ide

<dubbo:service timeout="4000" retries="0" interface="com.dingding.tms.bms.service.BillingZfbCodOrderService" ref="billingZfbCodOrderService" registry="globalRegistry"/>

conusmer中的配置:性能

<dubbo:reference id="billingInterService" interface="com.dingding.tms.bms.service.BillingInterService" protocol="dubbo" check="false" registry="globalRegistry" timeout="3000"/>

最後這個service在調用時的超時時間就是3秒

另外,超過3s後的表現以下:

(1) consumer會在超過3秒時獲得一個調用超時的異常。

(2) provider中代碼的執行不會由於超時而中斷,在執行完畢後,會獲得一個dubbo的警告。

 

3 關於dubbo的配置上,你有什麼經驗

(1) 在Provider上儘可能多地配置Consumer的屬性

緣由: 

a. 做爲服務的提供者,比服務使用方更清楚服務性能參數,如調用的超時時間,合理的重試次數等

b. 在provider配置後,consumer不配置則自動使用provider的配置,即做爲了consumer端的缺省值。不然,consumer會使用consumer端的全局配置,這對於provider端每每是不可控的。

ps: 配置的優先級:

1. 方法級配置別優於接口級別,即小Scope優先 
2. Consumer端配置優於Provider配置,優於全局配置 
3. Dubbo Hard Code的配置值(默認)

根據規則2, 縱使消費端配置優於服務器配置,但消費端配置超時時間不能爲所欲爲,須要根據業務實際狀況來設定。若是設置的過短,複雜業務原本就須要很長時間完成,服務端沒法在設定的超時時間內完成業務處理; 若是設置太長,會因爲服務端或者網絡問題致使客戶端大量線程掛起。

配置示例

<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0"/>
 
<dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0" >
    <dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" />
<dubbo:service/>

在provider端能夠配置的consumer屬性有:

1 timeout 方法調用超時時間

2 retry 失敗重試次數,缺省是2(加上第一次調用,共調用三次)

3 loadbalance 負責均衡算法(即多個provider如何挑選provider調用),缺省是隨機(random),還能夠有輪訓(roundrobin),最不活躍優先(leastactive, 指從consumer端併發調用效果最好的provider,這樣能夠相對減小併發的堆積)

4 actives 消費端最大併發調用限制,即當consumer對一個服務的併發調用達到上限後,新調用會wait直到超時。粒度上,在方法上配置(dubbo: method)則併發針對方法,在接口上配置(dubbo:service)則併發限制針對服務

 

(2) 在Provider上配置合理的provider屬性

<dubbo:protocol threads="200" />
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" executes="200" >
    <dubbo:method name="findAllPerson" executes="50" />
</dubbo:service>

Provider上能夠配置的Provider端屬性有:

  1. threads,服務線程池大小
  2. executes,一個服務提供者並行執行請求上限,即當Provider對一個服務的併發調用到上限後,新調用會wait(Consumer可能到超時)。在方法上配置(dubbo:method )則併發限制針對方法,在接口上配置(dubbo:service),則併發限制針對服務。

 

 

超時配置:

Dubbo消費端 
全局超時配置

<dubbo:consumer timeout="5000" />

指定接口以及特定方法超時配置

<dubbo:reference interface="com.foo.BarService" timeout="2000">
    <dubbo:method name="sayHello" timeout="3000" />
</dubbo:reference>

 

Dubbo服務端 
全局超時配置

<dubbo:provider timeout="5000" />

指定接口以及特定方法超時配置

<dubbo:provider interface="com.foo.BarService" timeout="2000">
    <dubbo:method name="sayHello" timeout="3000" />
</dubbo:provider>

 

4 dubbo協議超時實現

 Dubbo協議超時實現使用了Future模式,主要涉及類DubboInvoker,ResponseFuture, DefaultFuture。 
ResponseFuture.get()在請求還未處理完或未到超時前一直是wait狀態;響應達到後,設置請求狀態,並進行notify喚醒。

public Object get() throws RemotingException {
        return get(timeout);
    }

    public Object get(int timeout) throws RemotingException {
        if (timeout <= 0) {
            timeout = Constants.DEFAULT_TIMEOUT;
        }
        if (! isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (! isDone()) {
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (! isDone()) {
                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
            }
        }
        return returnFromResponse();
    }
View Code
    public static void received(Channel channel, Response response) {
        try {
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                future.doReceived(response);
            } else {
                logger.warn("The timeout response finally returned at " 
                            + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) 
                            + ", response " + response 
                            + (channel == null ? "" : ", channel: " + channel.getLocalAddress() 
                                + " -> " + channel.getRemoteAddress()));
            }
        } finally {
            CHANNELS.remove(response.getId());
        }
    }

    private void doReceived(Response res) {
        lock.lock();
        try {
            response = res;
            if (done != null) {
                done.signal();
            }
        } finally {
            lock.unlock();
        }
        if (callback != null) {
            invokeCallback(callback);
        }
    }
View Code

 

 

 

 

@see http://shiyanjun.cn/archives/325.html dubbo架構設計詳解

相關文章
相關標籤/搜索