我们目前在高效的服务器上遇到一些麻烦,因为它消耗的内存过多。其中一个漏洞可能来自球衣客户端。我发现了以下两个问题以及如何:
我从中获得了什么,我应该重用客户端,还可能重用WebTargets? 同时建议关闭回复,但我怎样才能使用.request()?
代码示例,每小时调用大约1000次,路径不同:
public byte[] getDocument(String path) {
Client client = ClientBuilder.newClient();
WebTarget target = client.target(config.getPublishHost() + path);
try {
byte[] bytes = target.request().get(byte[].class);
LOGGER.debug("Document size in bytes: " + bytes.length);
return bytes;
} catch (ProcessingException e) {
LOGGER.error(Constants.PROCESSING_ERROR, e);
throw new FailureException(Constants.PROCESSING_ERROR, e);
} catch (WebApplicationException e) {
LOGGER.error(Constants.RESPONSE_ERROR, e);
throw new FailureException(Constants.RESPONSE_ERROR, e);
} finally {
client.close();
}
}
所以我的问题是如何正确使用API来防止上述示例的泄漏?
答案 0 :(得分:6)
Client
个实例 Client
个实例是重量级对象,用于管理底层客户端通信基础架构。因此,初始化以及处理Client
实例可能是一项相当昂贵的操作。
documentation建议只创建小数 Client
个实例,尽可能重用它们。它还声明Client
实例必须正确关闭才能被处理以避免资源泄漏。
WebTarget
个实例可以重复使用如果对同一路径执行多个请求,则可以重用WebTarget
个实例。如果WebTarget
实例有configuration,则建议重复使用Response
个实例。
Response
个实例
应该关闭包含未消耗的实体输入流的answer个实例
。这通常仅适用于仅处理响应标头和状态代码的情况,忽略响应实体。有关关闭Response
个实例的详细信息,请参阅此Client
。
对于您问题中提到的情况,您希望确保Client
实例可以重用于所有getDocument(String)
方法调用。
例如,如果您的应用程序是基于CDI的,则在构造bean时创建一个Client
实例,并在销毁之前将其处置。在下面的示例中,WebTarget
实例存储在单个bean中:
@Singleton
public class MyBean {
private Client client;
@PostConstruct
public void onCreate() {
this.client = ClientBuilder.newClient();
}
...
@PreDestroy
public void onDestroy() {
this.client.close();
}
}
您不需要(或者可能不能)重用Response
实例(每个方法调用请求的路径更改)。当您将实体读入byte[]
时,answer实例会自动关闭。
连接池可以很好地改善性能。
正如我之前的HttpURLConnection
中所提到的,默认情况下,Jersey中的传输层由HttpUrlConnectorProvider
提供。此支持在泽西岛通过ApacheConnectorProvider
实施。如果需要,可以替换默认连接器,并使用连接池以获得更好的性能。
Jersey通过Client
与Apache HTTP Client集成。要使用它,请添加以下依赖项:
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-apache-connector</artifactId>
<version>2.26</version>
</dependency>
然后创建您的Jersey documentation about connectors实例,如下所示:
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(5);
ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
clientConfig.connectorProvider(new ApacheConnectorProvider());
Client client = ClientBuilder.newClient(clientConfig);
有关其他详细信息,请参阅:checked
。
答案 1 :(得分:1)
使用此链接中的以下示例关闭对completed
方法的回复:https://jersey.github.io/documentation/latest/async.html#d0e10209
final Future<Response> responseFuture = target().path("http://example.com/resource/")
.request().async().get(new InvocationCallback<Response>() {
@Override
public void completed(Response response) {
System.out.println("Response status code "
+ response.getStatus() + " received.");
//here you can close the response
}
@Override
public void failed(Throwable throwable) {
System.out.println("Invocation failed.");
throwable.printStackTrace();
}
});
只有当来自Response
类的类型时才能关闭响应,而不是:String
。
参考此question,当您阅读实体时,响应将自动关闭:
String responseAsString = response.readEntity(String.class);
参考此question,您可以使用连接池来获得更好的性能。例如:
public static JerseyClient getInstance() {
return InstanceHolder.INSTANCE;
}
private static class InstanceHolder {
private static final JerseyClient INSTANCE = createClient();
private static JerseyClient createClient() {
ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 200);
clientConfig.property(ClientProperties.READ_TIMEOUT, 10000);
clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 10000);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(100);
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
clientConfig.connectorProvider(new ApacheConnectorProvider());
JerseyClient client = JerseyClientBuilder.createClient(clientConfig);
//client.register(RequestLogger.requestLoggingFilter);
return client;
}
}
注意!使用此解决方案,如果您未关闭响应,则无法向服务器发送超过100个请求(setDefaultMaxPerRoute(100)
)