一、前言
前面講解了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中客戶端併發控制和服務端併發控制。另外想學併發的童鞋可以 單擊我