Vertx中HttpClient的setTimeout坑

问题描述:

因为特殊原因,导致某一时间段的请求全部超时,当网络恢复正常后,后续的请求还是会继续超时,且服务端也不能再收到任务消息,只能重启客户端服务器。

代码贴示:

private static HttpClientHolder clientHolder = new HttpClientHolder();
public static HttpClient getHttpClient() {
	return clientHolder.get(VertxInstance.getInstance().getVertx(),
			() -> new HttpClientOptions()
					.setMaxPoolSize(ConfigManager.DEFAULT.getInteger(WebGlobals.SECTION_NAME,
					"maxPool", 10));
}

private static void pushMsgToWebServer(JsonObject pushMsgRequest) {
    HttpClient client = getHttpClient();
    
    // Specify both port and host name
    HttpClientRequest clientRequest = client.post(WebGlobals.PORT, WebGlobals.HOST,
            WLGlobals.CALLBACK_URL_PATH, response -> {...});
    clientRequest.exceptionHandler(e -> {
        logger.error(String.format("pushMsgToWebServer response exception:%s", e));
    });
    clientRequest.setTimeout(30000);
    ......
}

原因说明:

首先我们需要理解HttpClient创建连接过程原理:池+队列,setMaxPoolSize是创建一个连接池,还有一个未使用方法setMaxWaitQueueSize用于创建队列大小(默认-1,表示无限大)。
当池被用完之后,新的请求会放入队列里等待。那么如果池的链接一直不归还(closed)那么后面新来的包括已经在等待的则无法被执行。
然后我们再看setTimeout()这个方法源码:

  public synchronized HttpClientRequest setTimeout(long timeoutMs) {
    cancelOutstandingTimeoutTimer();
    currentTimeoutMs = timeoutMs;
    currentTimeoutTimerId = client.getVertx().setTimer(timeoutMs, id -> handleTimeout(timeoutMs));
    return this;
  }

我们当调用这个方法时候,就已经开始进行超时计时(并不是从发出消息那刻算),并且继续深入源码会发现,该方法并无归还链接的操作。
即:一开始池中链接如果超时,并不会被主动释放(服务端调用closed时候,连接会被动释放),则后面依旧继续超时。

解决方案

在创建HttpClientOptions时候设置.setIdleTimeout(),源码中解释如下:
Set the idle timeout, default time unit is seconds. Zero means don't timeout.This determines if a connection will timeout and be closed if no data is received within the timeout.If you want change default time unit, use setIdleTimeoutUnit(TimeUnit)
即超时后会主动关闭连接

引用

https://github.com/eclipse-vertx/vert.x/issues/1842

你可能感兴趣的:(JAVA,Vertx)