本文將詳細(xì)分析< dubbo:service executes=”“/>與< dubbo:reference actives = “”/>的實(shí)現(xiàn)機(jī)制,深入探討Dubbo自身的保護(hù)機(jī)制。

1、源碼分析ExecuteLimitFilter

@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY )


過(guò)濾器作用

服務(wù)調(diào)用方并發(fā)度控制。

使用場(chǎng)景

對(duì)Dubbo服務(wù)提供者實(shí)現(xiàn)的一種保護(hù)機(jī)制,控制每個(gè)服務(wù)的最大并發(fā)度。

阻斷條件

當(dāng)服務(wù)調(diào)用超過(guò)允許的并發(fā)度后,直接拋出RpcException異常。

接下來(lái)源碼分析ExecuteLimitFilter的實(shí)現(xiàn)細(xì)節(jié)。

ExecuteLimitFilter#invoke


代碼@1:從服務(wù)提供者列表中獲取參數(shù)executes的值,如果該值小于等于0,表示不啟用并發(fā)度控制,直接沿著調(diào)用鏈進(jìn)行調(diào)用。

代碼@2:根據(jù)服務(wù)提供者url和服務(wù)調(diào)用方法名,獲取RpcStatus。


這里是并發(fā)容器ConcurrentHashMap的經(jīng)典使用,從 這里可以看出ConcurrentMap< String, ConcurrentMap< String, RpcStatus>> METHOD_STATISTICS的存儲(chǔ)結(jié)構(gòu)為 { 服務(wù)提供者URL唯一字符串:{方法名:RpcStatus} }。

代碼@3:根據(jù)服務(wù)提供者配置的最大并發(fā)度,創(chuàng)建該服務(wù)該方法對(duì)應(yīng)的信號(hào)量對(duì)象。


使用了雙重檢測(cè)來(lái)創(chuàng)建executesLimit 信號(hào)量。

代碼@4:如果獲取不到鎖,并不會(huì)阻塞等待,而是直接拋出RpcException,服務(wù)端的策略是快速拋出異常,供服務(wù)調(diào)用方(消費(fèi)者)根據(jù)集群策略進(jìn)行執(zhí)行,例如重試其他服務(wù)提供者。

代碼@5:執(zhí)行真實(shí)的服務(wù)調(diào)用。

代碼@6:如果成功申請(qǐng)到信號(hào)量,在服務(wù)調(diào)用結(jié)束后,釋放信號(hào)量。

總結(jié):< dubbo:service executes=”“/>的含義是,針對(duì)每個(gè)服務(wù)每個(gè)方法的最大并發(fā)度。如果超過(guò)該值,則直接拋出RpcException。


2、源碼分析ActiveLimitFilter

@Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY )


過(guò)濾器作用

消費(fèi)端調(diào)用服務(wù)的并發(fā)控制。

使用場(chǎng)景

控制同一個(gè)消費(fèi)端對(duì)服務(wù)端某一服務(wù)的并發(fā)調(diào)用度,通常該值應(yīng)該小于< dubbo:service executes=”“/>

阻斷條件

非阻斷,但如果超過(guò)允許的并發(fā)度會(huì)阻塞,超過(guò)超時(shí)時(shí)間后將不再調(diào)用服務(wù),而是直接拋出超時(shí)。

源碼分析ActiveLimitFilter的實(shí)現(xiàn)原理:

ActiveLimitFilter#invoke

代碼@1:從Invoker中獲取消息端URL中的配置的actives參數(shù),為什么從Invoker中獲取的Url是消費(fèi)端的Url呢?這是因?yàn)樵谙M(fèi)端根據(jù)服務(wù)提供者URL創(chuàng)建調(diào)用Invoker時(shí),會(huì)用服務(wù)提供者URL,然后合并消費(fèi)端的配置屬性,其優(yōu)先級(jí) -D > 消費(fèi)端 > 服務(wù)端。其代碼位于:、

RegistryDirectory#toInvokers

URL url = mergeUrl(providerUrl);

代碼@2:根據(jù)服務(wù)提供者URL和調(diào)用服務(wù)提供者方法,獲取RpcStatus。

代碼@3:獲取接口調(diào)用的超時(shí)時(shí)間,默認(rèn)為1s。

代碼@4:獲取當(dāng)前消費(fèi)者,針對(duì)特定服務(wù),特定方法的并發(fā)調(diào)用度,active值。

代碼@5:如果當(dāng)前的并發(fā) 調(diào)用大于等于允許的最大值,則針對(duì)該RpcStatus申請(qǐng)鎖,并調(diào)用其wait(timeout)進(jìn)行等待,也就是在接口調(diào)用超時(shí)時(shí)間內(nèi),還是未被喚醒,則直接拋出超時(shí)異常。

代碼@6:判斷被喚醒的原因是因?yàn)榈却瑫r(shí),還是由于調(diào)用結(jié)束,釋放了”名額“,如果是超時(shí)喚醒,則直接拋出異常。

代碼@7:在一次服務(wù)調(diào)用前,先將 服務(wù)名+方法名對(duì)應(yīng)的RpcStatus的active加一。

代碼@8:執(zhí)行RPC服務(wù)調(diào)用。

代碼@9:記錄成功調(diào)用或失敗調(diào)用,并將active減一。

代碼@10:最終成功執(zhí)行,如果開(kāi)啟了actives機(jī)制(dubbo:referecnce actives=”“)時(shí),喚醒等待者。

總結(jié):< dubbo:reference actives=”“/> 是控制消費(fèi)端對(duì) 單個(gè)服務(wù)提供者單個(gè)服務(wù)允許調(diào)用的最大并發(fā)度。該值的取值不應(yīng)該大于< dubbo:service executes=”“/>的值,并且如果消費(fèi)者機(jī)器的配置,如果性能不盡相同,不建議對(duì)該值進(jìn)行設(shè)置。