According to the documentation,
根据文档,
"Clients are heavy-weight objects that manage the client-side communication infrastructure. Initialization as well as disposal of a Client instance may be a rather expensive operation. It is therefore advised to construct only a small number of Client instances in the application. "
客户端是管理客户端通信基础设施的重量级对象。初始化和处理客户端实例可能是相当昂贵的操作。因此,建议在应用程序中只构造少量的客户端实例。”
Ok, I'm trying to cache Client itself and WebTarget instances in a static variable, the someMethod() is invoked in multi-threaded environment:
好的,我正在尝试在静态变量中缓存客户端本身和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;
}
But sometimes (not always) I'm get an exception:
但有时(并非总是)我也有例外:
Invalid use of BasicClientConnManager: connection still allocated. Make sure to release the connection before allocating another one.
BasicClientConnManager的使用无效:连接仍然被分配。在分配另一个连接之前,请确保释放该连接。
How can Client/WebTarget be reused/cached correctly? Is it possible with JAX RS Client API? Or I have to use some framework-specific features (resteasy/jersey) Could you provide some example or documentation?
如何正确地重用/缓存客户端/WebTarget ?使用JAX RS客户端API是否可能?或者我必须使用一些特定于框架的特性(resteasy/jersey),您能提供一些示例或文档吗?
3 个解决方案
#1
5
Your implementation is not thread-safe. When two threads access someMethod
at the same time they are sharing the same Client
and one will try to make a second request while the first one is not finished.
您的实现不是线程安全的。当两个线程同时访问someMethod时,它们共享同一个客户机,一个线程将尝试发出第二个请求,而第一个请求尚未完成。
You have two choices:
你有两个选择:
- Synchronize the access to the
Client
andWebTarget
manually. - 手动同步对客户端和WebTarget的访问。
- Let the container manage concurrency by annotating the enclosing type with
@javax.ejb.Singleton
which guarantees thread safety. (see chapter 4.8.5 of the EJB specification) - 通过使用@javax.ejb注释封闭类型,让容器管理并发。单例,保证线程安全。(参见EJB规范第4.8.5章)
If someMethod
in a container managed environment I would use the second approach.
如果在容器管理环境中有someMethod,那么我将使用第二种方法。
#2
5
Since this issue is still open at the time of writing (version 3.0.X) RESTEASY: deprecated Apache classes cleanup
由于这个问题在编写RESTEASY:已废弃的Apache类清理时仍然存在(version 3.0.X)
You can go deeper to use the newer, non-deprecated classes instead to create you resteasy client. You will also have more control over how you want the pool to be etc.
您可以更深入地使用更新的、未弃用的类来创建resteasy客户端。您还将有更多的控制权如何您想要的池等。
Here is what I did:
我所做的是:
// 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();
Also make sure you release the connection after making a call. Calling response.close() will do that for you so probably put that in a finally block.
还要确保在打电话后释放连接。调用response.close()会这样做,所以很可能会把它放到最后一个块中。
#3
3
First, do not reuse WebTarget. For simplicity, you can always create new WebTarget.
首先,不要重用WebTarget。为了简单起见,您总是可以创建新的WebTarget。
Second, if you're using Resteasy, you can add provided dependency for Resteasy client to your project. Example in Gradle:
其次,如果您正在使用Resteasy,您可以将Resteasy客户端提供的依赖项添加到您的项目中。在它的例子:
provided 'org.jboss.resteasy:resteasy-client:3.0.14.Final'
Then, you can create your connection like this:
然后,您可以创建这样的连接:
ResteasyClientBuilder builder = new ResteasyClientBuilder();
builder.connectionPoolSize(200);
There is no need to set maxPooledPerRoute, this is set automatically by RestEasy (can be found in RestEasyClientBuilder class source code).
不需要设置maxPooledPerRoute,这是RestEasy自动设置的(可以在RestEasyClientBuilder类源代码中找到)。
When you set connectionPoolSize, you will no longer get error when Client is reused and you can happily re-use them all across the application. I've tried this solution on many projects and it actually works well. But when you deploy your application to a non-resteasy container (like Glassfish), your code won't work and you will have to use ClientBuilder class again.
当您设置connectionPoolSize时,当客户端被重用时,您将不再会得到错误,您可以在整个应用程序中愉快地重用它们。我在很多项目上都尝试过这个方案,而且效果很好。但是,当您将应用程序部署到非resteasy容器(如Glassfish)时,您的代码将无法工作,您将不得不再次使用ClientBuilder类。
#1
5
Your implementation is not thread-safe. When two threads access someMethod
at the same time they are sharing the same Client
and one will try to make a second request while the first one is not finished.
您的实现不是线程安全的。当两个线程同时访问someMethod时,它们共享同一个客户机,一个线程将尝试发出第二个请求,而第一个请求尚未完成。
You have two choices:
你有两个选择:
- Synchronize the access to the
Client
andWebTarget
manually. - 手动同步对客户端和WebTarget的访问。
- Let the container manage concurrency by annotating the enclosing type with
@javax.ejb.Singleton
which guarantees thread safety. (see chapter 4.8.5 of the EJB specification) - 通过使用@javax.ejb注释封闭类型,让容器管理并发。单例,保证线程安全。(参见EJB规范第4.8.5章)
If someMethod
in a container managed environment I would use the second approach.
如果在容器管理环境中有someMethod,那么我将使用第二种方法。
#2
5
Since this issue is still open at the time of writing (version 3.0.X) RESTEASY: deprecated Apache classes cleanup
由于这个问题在编写RESTEASY:已废弃的Apache类清理时仍然存在(version 3.0.X)
You can go deeper to use the newer, non-deprecated classes instead to create you resteasy client. You will also have more control over how you want the pool to be etc.
您可以更深入地使用更新的、未弃用的类来创建resteasy客户端。您还将有更多的控制权如何您想要的池等。
Here is what I did:
我所做的是:
// 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();
Also make sure you release the connection after making a call. Calling response.close() will do that for you so probably put that in a finally block.
还要确保在打电话后释放连接。调用response.close()会这样做,所以很可能会把它放到最后一个块中。
#3
3
First, do not reuse WebTarget. For simplicity, you can always create new WebTarget.
首先,不要重用WebTarget。为了简单起见,您总是可以创建新的WebTarget。
Second, if you're using Resteasy, you can add provided dependency for Resteasy client to your project. Example in Gradle:
其次,如果您正在使用Resteasy,您可以将Resteasy客户端提供的依赖项添加到您的项目中。在它的例子:
provided 'org.jboss.resteasy:resteasy-client:3.0.14.Final'
Then, you can create your connection like this:
然后,您可以创建这样的连接:
ResteasyClientBuilder builder = new ResteasyClientBuilder();
builder.connectionPoolSize(200);
There is no need to set maxPooledPerRoute, this is set automatically by RestEasy (can be found in RestEasyClientBuilder class source code).
不需要设置maxPooledPerRoute,这是RestEasy自动设置的(可以在RestEasyClientBuilder类源代码中找到)。
When you set connectionPoolSize, you will no longer get error when Client is reused and you can happily re-use them all across the application. I've tried this solution on many projects and it actually works well. But when you deploy your application to a non-resteasy container (like Glassfish), your code won't work and you will have to use ClientBuilder class again.
当您设置connectionPoolSize时,当客户端被重用时,您将不再会得到错误,您可以在整个应用程序中愉快地重用它们。我在很多项目上都尝试过这个方案,而且效果很好。但是,当您将应用程序部署到非resteasy容器(如Glassfish)时,您的代码将无法工作,您将不得不再次使用ClientBuilder类。