使用jersey的JAX RS的内存问题

我们目前在高效的服务器上遇到了一些麻烦,因为它消耗的内存过多。 其中一个漏洞可能来自jersey客户端。 我发现了以下两个问题以及如何:

  1. 如何正确共享JAX-RS 2.0客户端
  2. 关闭JAX RS客户端/响应
  3. https://blogs.oracle.com/japod/entry/how_to_use_jersey_client

我从中获得了什么,我应该重用客户端,还可能重用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​​来防止上述示例的泄漏?

应重用Client实例

Client实例是重量级对象,用于管理底层客户端通信基础结构。 因此,初始化以及Client实例的处置可能是相当昂贵的操作。

该文档建议仅创建少量 Client实例并在可能的情况下重用它们 。 它还声明Client实例必须在处置之前正确关闭,以避免泄漏资源。

WebTarget实例可以重用

如果对同一路径执行多个请求,则可以重用WebTarget实例。 如果他们有一些配置,建议重用WebTarget实例。

如果您不读取实体,则应关闭Response实例

应关闭包含未使用的实体输入流的Response实例。 这通常仅适用于仅处理响应标头和状态代码的情况, 忽略响应实体 。 有关关闭Response实例的更多详细信息,请参阅此答案

改善你的代码

对于您的问题中提到的情况,您希望确保为所有getDocument(String)方法调用重用Client实例。

例如,如果您的应用程序是基于CDI的,则在构造bean时创建一个Client实例,并在销毁之前将其处置。 在下面的示例中, Client实例存储在单例bean中:

 @Singleton public class MyBean { private Client client; @PostConstruct public void onCreate() { this.client = ClientBuilder.newClient(); } ... @PreDestroy public void onDestroy() { this.client.close(); } } 

您不需要(或者可能不能)重用WebTarget实例(每个方法调用请求的路径更改)。 当您将实体读入byte[]时, Response实例将自动关闭。

使用连接池

连接池可以很好地提高性能。

正如我在旧答案中所提到的,默认情况下,Jersey中的传输层由HttpURLConnection提供。 此支持通过HttpUrlConnectorProvider在Jersey中实现。 如果需要,可以替换默认连接器并使用连接池以获得更好的性能。

Jersey通过ApacheConnectorProvider与Apache HTTP Client集成。 要使用它,请添加以下依赖项:

  org.glassfish.jersey.connectors jersey-apache-connector 2.26  

然后按如下方式创建Client实例:

 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); 

有关其他详细信息,请参阅有关连接器的Jersey文档 。

使用此链接中的以下示例关闭completed方法的响应: https : //jersey.github.io/documentation/latest/async.html#d0e10209

  final Future responseFuture = target().path("http://example.com/resource/") .request().async().get(new InvocationCallback() { @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(); } }); 

提示1(响应或字符串):

只有当它来自Response类的类型时才能关闭Response ,而不是: String

提示2(自动关闭):

参考此问题 ,当您阅读实体时,响应将自动关闭:

 String responseAsString = response.readEntity(String.class); 

提示3(连接池):

参考此问题 ,您可以使用连接池来获得更好的性能。 例:

 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)