if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RpcException(“unexpected exception when ExecuteLimitFilter”, t);
}
} finally {
if(acquireResult) { // @6
executesLimit.release();
}
}
}
代码@1:从服务提供者列表中获取参数executes的值,如果该值小于等于0,表示不启用并发度控制,直接沿着调用链进行调用。
代码@2:根据服务提供者url和服务调用方法名,获取RpcStatus。
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;
}
这里是并发容器ConcurrentHashMap的经典使用,从 这里可以看出ConcurrentMap< String, ConcurrentMap< String, RpcStatus>> METHOD_STATISTICS的存储结构为 { 服务提供者URL唯一字符串:{方法名:RpcStatus} }。
代码@3:根据服务提供者配置的最大并发度,创建该服务该方法对应的信号量对象。
public Semaphore getSemaphore(int maxThreadNum) {
if(maxThreadNum <= 0) {
return null;
}
if (executesLimit == null || executesPermits != maxThreadNum) {
synchronized (this) {
if (executesLimit == null || executesPermits != maxThreadNum) {
executesLimit = new Semaphore(maxThreadNum);
executesPermits = maxThreadNum;
}
}
}
return executesLimit;
}
使用了双重检测来创建executesLimit 信号量。
代码@4:如果获取不到锁,并不会阻塞等待,而是直接抛出RpcException,服务端的策略是快速抛出异常,供服务调用方(消费者)根据集群策略进行执行,例如重试其他服务提供者。
代码@5:执行真实的服务调用。
代码@6:如果成功申请到信号量,在服务调用结束后,释放信号量。
总结:< dubbo:service executes=“”/>的含义是,针对每个服务每个方法的最大并发度。如果超过该值,则直接抛出RpcException。
2、源码分析ActiveLimitFilter
@Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY )
- 过滤器作用
消费端调用服务的并发控制。
- 使用场景
控制同一个消费端对服务端某一服务的并发调用度,通常该值应该小于< dubbo:service executes=“”/>
- 阻断条件
非阻断,但如果超过允许的并发度会阻塞,超过超时时间后将不再调用服务,而是直接抛出超时。
源码分析ActiveLimitFilter的实现原理:
ActiveLimitFilter#invoke
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); // @1
RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()); // @2
if (max > 0) {
long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0); // @3
long start = System.currentTimeMillis();
long remain = timeout;
int active = count.getActive(); // @4
if (active >= max) { // @5
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) { // @6
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); // @7
try {
Result result = invoker.invoke(invocation); // @8
RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true); // @9
return result;
最后
我还为大家准备了一套体系化的架构师学习资料包以及BAT面试资料,供大家参考及学习
已经将知识体系整理好(源码,笔记,PPT,学习视频)
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
家参考及学习
已经将知识体系整理好(源码,笔记,PPT,学习视频)
[外链图片转存中…(img-WkCREdqE-1725611959904)]
[外链图片转存中…(img-UzM8w17J-1725611959905)]
[外链图片转存中…(img-hCjX4TS3-1725611959905)]
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0