如果应该始终正确关闭响应,如何重构代码?

时间:2015-01-28 05:00:45

标签: java design-patterns apache-httpclient-4.x

在我们的一个项目中,我们需要使用httpClient从后端服务中获取一些数据。我们发现遗留代码没有正确关闭响应。

代码如下:

HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
    return EntityUtils.toString(response.getEntity());
} else {
    throw new InvalidResponseException();
}

statusCode200时,EntityUtils.toString将使用响应的内容,然后正确关闭它。但在其他情况下,响应没有关闭,我们将有http连接泄漏(一段时间后,httpClient池运行,我们无法获得新线程)

代码库上有很多这样的代码,所以我想使用加载设计模式来简化它。

我定义了一个HttpClientWrapper,如:

class HttpClientWrapper {
    private HttpClient client;

    public HttpClientWrapper(HttpClient client) {
        this.client = client;
    }

    public <T> T execute(HttpRequestBase request, WithResponse<T> handler) {
        HttpResponse response = null;
        try {
            response = client.execute(request);
            return handler.withResponse(response);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (response != null) {
                EntityUtils.consumeQuietly(response.getEntity());
            }
        }
    }
}

interface WithResponse<T> {
    T withResponse(HttpResponse response) throws Exception;
}

我在finally的包装器中消耗了响应,因此响应将始终正确关闭。我可以愉快地使用它更改现有代码:

return new HttpClientWrapper(httpClient).execute(request, new WithResponse<String>() {
    String withResponse(HttpResponse response) throws Exception {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            return EntityUtils.toString(response.getEntity());
        } else {
            throw new InvalidResponseException();
        }
    }
});

我不再需要担心泄漏。

但突然间我发现了这么一段代码:

Stopwatch stopwatch = new Stopwatch();

try {
    stopwatch.start();
    HttpResponse response = httpClient.execute( request );
    stopwatch.stop();

    MDC.put( "backendTime", String.valueOf( stopwatch.elapsed( TimeUnit.MILLISECONDS ) ) );

    return EntityUtils.toString(response.getEntity());

} catch( IOException e ) {
    throw new RuntimeException( e );
}

需要检查httpClient用于获取响应的时间长度!我不能在这里使用HttpClientWrapper,因为我无法找到一种方法来衡量每个当前设计的整个过程的一部分。

我现在有两个选择:

  1. 不要将HttpClientWrapper用于此代码,我们需要手动关闭响应。 (但获得回应的方式不再一致)

  2. 修改HttpClientWrapper,使其变得复杂并且足够灵活以满足此要求。 (但只有一个地方需要它)

  3. 我不喜欢,还有其他更好的解决方案吗?

1 个答案:

答案 0 :(得分:2)

是什么阻止您在包装器之前初始化StopWatch并在回调中停止它?

final Stopwatch stopwatch = new Stopwatch();
stopwatch.start();

new HttpClientWrapper(httpClient).execute(request, new WithResponse<String>() {
    String withResponse(HttpResponse response) throws Exception {

        stopwatch.stop();
        MDC.put( "backendTime", String.valueOf( stopwatch.elapsed( TimeUnit.MILLISECONDS ) ) );

        // ...
    }
});