在多线程环境中重用 JAX RS 客户机(使用 resteasy)

2022-09-02 13:28:23

根据文件,

“客户端是管理客户端通信基础架构的重物。客户端实例的初始化和处置可能是一个相当昂贵的操作。因此,建议在应用程序中仅构造少量的客户端实例。"

好的,我正在尝试在静态变量中缓存客户端本身和WebTarget实例,someMethod()在多线程环境中调用:

private static Client client = ClientBuilder.newClient();
private static WebTarget webTarget = client.target("someBaseUrl");
...
public static String someMethod(String arg1, String arg2)
{
    WebTarget target = entrTarget.queryParam("arg1", arg1).queryParam("arg2", arg2);
    Response response = target.request().get();
    final String result = response.readEntity(String.class);
    response.close();
    return result;
}

但有时(并非总是)我会遇到一个例外:

基本客户端管理无效使用:连接仍已分配。请确保在分配另一个连接之前释放连接。

如何正确重用/缓存客户端/WebTarget?JAX RS 客户机 API 是否可行?或者我必须使用一些特定于框架的功能(resteasy/jersey),你能提供一些例子或文档吗?


答案 1

您的实现不是线程安全的。当两个线程同时访问它们共享相同的线程时,一个线程将尝试在第一个线程未完成时发出第二个请求。someMethodClient

您有两种选择:

  • 同步访问和手动。ClientWebTarget
  • 让容器通过注释用于保证线程安全的封闭类型来管理并发性。(参见 EJB 规范的第 4.8.5 章@javax.ejb.Singleton)

如果在容器管理的环境中,我会使用第二种方法。someMethod


答案 2

由于在撰写本文时(版本3.0.X)此问题仍处于打开状态,因此 RESTEASY:已弃用的Apache类清理

您可以更深入地使用较新的、未弃用的类来创建令人不安的客户端。您还可以更好地控制您希望的游泳池等。

以下是我所做的:

// This will create a threadsafe JAX-RS client using pooled connections.
// Per default this implementation will create no more than than 2
// concurrent connections per given route and no more 20 connections in
// total. (see javadoc of PoolingHttpClientConnectionManager)
PoolingHttpClientConnectionManager cm =
        new PoolingHttpClientConnectionManager();

CloseableHttpClient closeableHttpClient =
        HttpClientBuilder.create().setConnectionManager(cm).build();
ApacheHttpClient4Engine engine =
        new ApacheHttpClient4Engine(closeableHttpClient);
return new ResteasyClientBuilder().httpEngine(engine).build();

此外,请确保在拨打电话后解除连接。调用responser.close()将为您执行此操作,因此可能会将其放在最终的块中。