在multithreading环境中重用JAX RS Client(具有resteasy)

根据文件,

“客户端是管理客户端通信基础设施的重量级对象。初始化以及客户端实例的处理可能是一项相当昂贵的操作。因此建议在应用程序中仅构建少量客户端实例。”

好吧,我试图在一个静态变量中缓存Client本身和WebTarget实例,在multithreading环境中调用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; } 

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

BasicClientConnManager的使用无效:仍然分配了连接。 确保在分配另一个连接之前释放连接。

如何正确地重用/缓存Client / WebTarget? 是否可以使用JAX RS Client API? 或者我必须使用一些特定于框架的function(resteasy / jersey)你能提供一些示例或文档吗?

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

你有两个选择:

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

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

由于此问题在编写时仍然是开放的(版本3.0.X) RESTEASY:不推荐使用的Apache类清理

您可以更深入地使用较新的,不推荐使用的类来创建resteasy客户端。 您还可以更好地控制池的使用方式等。

这是我做的:

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

还要确保在拨打电话后释放连接 。 调用response.close()会为你做这个,所以可能把它放在finally块中。

首先,不要重用WebTarget。 为简单起见,您始终可以创建新的WebTarget。

其次,如果您正在使用Resteasy,则可以将Resteasy客户端提供的依赖项添加到您的项目中。 Gradle中的示例:

  provided 'org.jboss.resteasy:resteasy-client:3.0.14.Final' 

然后,您可以像这样创建连接:

  ResteasyClientBuilder builder = new ResteasyClientBuilder(); builder.connectionPoolSize(200); 

没有必要设置maxPooledPerRoute,这是由RestEasy自动设置的(可以在RestEasyClientBuilder类源代码中找到)。

设置connectionPoolSize时,在重用Client时不会再出错,您可以在整个应用程序中重复使用它们。 我在许多项目中尝试过这个解决方案,但实际上效果很好。 但是当您将应用程序部署到非resteasy容器(如Glassfish)时,您的代码将无法运行,您将不得不再次使用ClientBuilder类。