使用jersey的JAX RS的内存问题

时间:2017-04-05 10:06:55

标签: java performance rest jersey java-memory-leaks

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

  1. How to correctly share JAX-RS 2.0 client
  2. Closing JAX RS Client/Response
  3. https://blogs.oracle.com/japod/entry/how_to_use_jersey_client
  4. 我从中获得了什么,我应该重用客户端,还可能重用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​​来防止上述示例的泄漏?

2 个答案:

答案 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();
                }
            });

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

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

提示2(自动关闭):

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

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

提示3(连接池):

参考此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)