一、消费者并发控制
消费者对同一个方法可以进行并发数的控制,dubbo也是基于过滤器的设置功能来实现并发控制的。
通过配置actives的值,在可以Consumer配置,也可以在Provider配置,一般可以在提供者配置的,可以在提供者配置,提供者比较清楚服务能够承受的并发数。
actives默认 0 每服务消费者每服务每方法最大并发调用数
@Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)
public class ActiveLimitFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
String methodName = invocation.getMethodName();
int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0); //21处,获取actives配置项的值
RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()); //22处
if (max > 0) {
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) { //23处
synchronized (count) { //24处
while ((active = count.getActive()) >= max) {
try {
count.wait(remain); //25处
} catch (InterruptedException e) {
}
long elapsed = System.currentTimeMillis() - start;
remain = timeout - elapsed; //26处
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();
}
}
}
}
}
在"21处",获取配置的属性值,然后"22处",获取方法的并发数。
RpcStatus使用一个map保存了方法的并发数:
public static RpcStatus getStatus(URL url, String methodName) {
String uri = url.toIdentityString();
ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
if (map == null) {
METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
map = METHOD_STATISTICS.get(uri);
}
RpcStatus status = map.get(methodName);
if (status == null) {
map.putIfAbsent(methodName, new RpcStatus());
status = map.get(methodName);
}
return status;
}
在上面的"23处",如果方法调用数已经达到 最大并发数,则"24处"会使用锁,使得消息者进行排队,获得锁后,并且 并发数 还是 达到并发数的话,"25处"会等待,"26处"判断是否等等超时,超时则抛出超时异常。
二、提供者并发控制
提供者对同一个方法可以进行并发数的控制,dubbo也是基于过滤器的设置功能来实现并发控制的。
可能通过配置executes属性来控制提供者服务被调用的数量
executes 默认0 服务提供者每服务每方法最大可并行执行请求数
下面是源码
@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
public class ExecuteLimitFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
String methodName = invocation.getMethodName();
int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
if (max > 0) { //31处
RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
if (count.getActive() >= max) { //32处
throw new RpcException("Failed to invoke method " + invocation.getMethodName() + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");
}
}
long begin = System.currentTimeMillis();
boolean isException = false;
RpcStatus.beginCount(url, methodName);
try {
Result result = invoker.invoke(invocation);
return result;
} catch (Throwable t) {
isException = true;
if(t instanceof RuntimeException) {
throw (RuntimeException) t;
}
else {
throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
}
}
finally {
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isException);
}
}
}
从上面源码的31处,32处,可以知道提供者的并发控制 和 消费者的并发控制 有点类似,不同的是,提供者一旦达到最大并发数,不会有排队超时机制,提供者会马止抛出异常。
额外还有一个配置属性connections,默认值100,每个服务对每个提供者的最大连接数,rmi、http、hessian等短连接协议支持此配置,dubbo协议长连接不支持此配置
自己写了个RPC:
可以给个star,^0^.