问题:
最近开发一个新环境的多账单查询需求,部署测试环境之后,偶尔出现账单查询失败,查询日志,如下:
解释:当前服务调用超时,且达到最大并发调用限制
思考:
最大并发调用限制20是很容易达到的,使用的是同一套服务代码,为何其他环境没有出现这个问题呢?为何线上也没有出现这个问题呢?猜测应该是新加的配置导致的这个问题
引入:
ActiveLimitFilter
查看ActiveLimitFilter源码,发现这个异常抛出的由来.服务使用dubbo作为rpc框架,dubbo针对消费者端提供了ActiveLimitFilter 类来限制最大并发数量,保障服务提供者的稳定运行。
下面看源码解析
@Activate(group = CONSUMER)
public class ActiveLimitFilter extends ListenableFilter {
private static final String ACTIVELIMIT_FILTER_START_TIME = "activelimit_filter_start_time";
public ActiveLimitFilter() {
super.listener = new ActiveLimitListener();
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//这个是服务提供者的url对象,保存了很多信息,如:地址,端口,超时时间,最大调用次数等
URL url = invoker.getUrl();
//服务方法
String methodName = invocation.getMethodName();
//获取此服务此方法所允许的最大并发调用数
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
//rpcStatus
final RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
//如果当前并发调用数大于最大并发限制
if (!RpcStatus.beginCount(url, methodName, max)) {
//获取超时时间
long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 0);
long start = System.currentTimeMillis();
long remain = timeout;
synchronized (rpcStatus) {
//当前并发调用数依旧大于最大并发限制
while (!RpcStatus.beginCount(url, methodName, max)) {
try {
//加入等待队列
rpcStatus.wait(remain);
} catch (InterruptedException e) {
// ignore
}
long elapsed = System.currentTimeMillis() - start;
remain = timeout - elapsed;
//超过超时时间
if (remain <= 0) {
throw new RpcException(RpcException.LIMIT_EXCEEDED_EXCEPTION,
"Waiting concurrent invoke timeout in client-side for service: " +
invoker.getInterface().getName() + ", method: " + invocation.getMethodName() +
", elapsed: " + elapsed + ", timeout: " + timeout + ". concurrent invokes: " +
rpcStatus.getActive() + ". max concurrent invoke limit: " + max);
}
}
}
}
invocation.setAttachment(ACTIVELIMIT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
return invoker.invoke(invocation);
}
}
问题来源:
每服务消费者达到每服务每方法最大并发调用数,且等待时间大于配置超时时间。原因是引入的公司定时任务组件默认配置20,导致出现此异常
问题解决
1,去除导致此异常的定时任务组件,不实际,项目需要引入定时任务。
2,给对应服务提供者配置actives,问题解决
@Reference(actives = 50)
private IHelloProviderService iHelloProviderService;
总结:
当当前执行该方法的线程超出了最大限制,那么可以等待一个timeout时间,如果时间过了还是超出了最大限制,那么就抛出异常。