Dubbo剖析-併發控制

一、前言

前面講解了Dubbo的服務降級,本節我們來講解dubbo中的併發控制,併發控制分爲客戶端併發控制和服務端併發控制。

二、併發控制

2.1 客戶端併發控制

在服務消費方法進行併發控制需要設置actives參數,如下:

<dubbo:reference id="userService" interface="com.test.UserServiceBo"
        group="dubbo" version="1.0.0" timeout="3000" actives="10"/>

設置com.test.UserServiceBo接口中所有方法,每個方法最多同時併發請求10個請求。

也可以使用下面方法設置接口中的單個方法的併發請求個數,如下:


    <dubbo:reference id="userService" interface="com.test.UserServiceBo"
        group="dubbo" version="1.0.0" timeout="3000">
                <dubbo:method name="sayHello" actives="10" />
    </dubbo:reference>

如上設置sayHello方法的併發請求數量最大爲10,如果客戶端請求該方法併發超過了10則客戶端會被阻塞,等客戶端併發請求數量少於10的時候,該請求才會被髮送到服務提供方服務器。在dubbo中客戶端併發控制是使用ActiveLimitFilter過濾器來控制的,代碼如下:

public class ActiveLimitFilter implements Filter {

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        URL url = invoker.getUrl();
        String methodName = invocation.getMethodName();
        //獲取設置的acvites的值,默認爲0
        int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
        //獲取當前方法目前併發請求數量
        RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
        if (max > 0) {//說明設置了actives變量
            long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
            long start = System.currentTimeMillis();
            long remain = timeout;
            int active = count.getActive();
            //如果該方法併發請求數量大於設置值,則掛起當前線程。
            if (active >= max) {
                synchronized (count) {
                    while ((active = count.getActive()) >= max) {
                        try {
                            count.wait(remain);
                        } catch (InterruptedException e) {
                        }
                        //如果等待時間超時,則拋出異常
                        long elapsed = System.currentTimeMillis() - start;
                        remain = timeout - elapsed;
                        if (remain <= 0) {
                            throw new RpcException("Waiting concurrent invoke timeout in client-side for service:  "
                                    + invoker.getInterface().getName() + ", method: "
                                    + invocation.getMethodName() + ", elapsed: " + elapsed
                                    + ", timeout: " + timeout + ". concurrent invokes: " + active
                                    + ". max concurrent invoke limit: " + max);
                        }
                    }
                }
            }
        }
        //沒有限流時候,正常調用
        try {
            long begin = System.currentTimeMillis();
            RpcStatus.beginCount(url, methodName);
            try {
                Result result = invoker.invoke(invocation);
                RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
                return result;
            } catch (RuntimeException t) {
                RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
                throw t;
            }
        } finally {
            if (max > 0) {
                synchronized (count) {
                    count.notify();
                }
            }
        }
    }

}

可知客戶端併發控制,是如果當併發量達到指定值後,當前客戶端請求線程會被掛起,如果在等待超時期間併發請求量少了,那麼阻塞的線程會被激活,然後發送請求到服務提供方,如果等待超時了,則直接拋出異常,這時候服務根本都沒有發送到服務提供方服務器。

2.2 服務端併發控制

在服務提供方進行併發控制需要設置executes參數,如下:

        <dubbo:service interface="com.test.UserServiceBo" ref="userService"
            group="dubbo"  version="1.0.0" timeout="3000" executes="10"/>

設置com.test.UserServiceBo接口中所有方法,每個方法最多同時併發處理10個請求,這裏併發是指同時在處理10個請求。

也可以使用下面方法設置接口中的單個方法的併發處理個數,如下:



        <dubbo:service interface="com.test.UserServiceBo" ref="userService"
            group="dubbo" version="1.0.0" timeout="3000" >
            <dubbo:method name="sayHello" executes="10" />
        </dubbo:service>

如上設置sayHello方法的併發處理數量爲10.

需要注意的是,服務提供方設置併發數量後,如果同時請求數量大於了設置的executes的值,則會拋出異常,而不是像消費端設置actives時候,會等待。服務提供方併發控制是使用ExecuteLimitFilter過濾器實現的,ExecuteLimitFilter代碼如下:

    public class ExecuteLimitFilter implements Filter {

        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            URL url = invoker.getUrl();
            //默認不設置executes時候,其值爲0
            int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
            if (max > 0) {//max>0說明設置了executes值
                RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
                //可知如果併發處理數量大於設置的值,會拋出異常
                executesLimit = count.getSemaphore(max);
                if(executesLimit != null && !(acquireResult = executesLimit.tryAcquire())) {
                    throw new RpcException("Failed to invoke method " + invocation.getMethodName() + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");
                }
            }
            ...
            try {//沒有限流時候,激活filter鏈
                Result result = invoker.invoke(invocation);
                return result;
            } catch (Throwable t) {
             ...
            } finally {
               ...
            }
        }
    }

所以當使用executes參數時候要注意,當併發量過大時候,多餘的請求會失敗。

三、總結

本節我們講解了dubbo中客戶端併發控制和服務端併發控制。另外想學併發的童鞋可以 單擊我

發佈了112 篇原創文章 · 獲贊 34 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章