RestEasy客户端:BasicClient ConnManager的使用无效:连接仍在分配中
我正在使用RestEasy客户端从web服务器检索实体列表。这是我的代码:RestEasy客户端:BasicClient ConnManager的使用无效:连接仍在分配中,resteasy,apache-httpclient-4.x,Resteasy,Apache Httpclient 4.x,我正在使用RestEasy客户端从web服务器检索实体列表。这是我的代码: @ApplicationScoped public class RestHttpClient { private ResteasyClient client; @Inject private ObjectMapper mapper; @PostConstruct public void initialize() { HttpParams params = new
@ApplicationScoped
public class RestHttpClient {
private ResteasyClient client;
@Inject
private ObjectMapper mapper;
@PostConstruct
public void initialize() {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 5000);
HttpConnectionParams.setSoTimeout(params, 5000);
HttpClient httpClient = new DefaultHttpClient(params);
this.client = new ResteasyClientBuilder().httpEngine(new ApacheHttpClient4Engine(httpClient)).build();
}
public <E> List<E> getList(final Class<E> resultClass, final String path,
MultivaluedMap<String, Object> queryParams) {
ResteasyWebTarget target = this.client.target(path);
Response response = null;
try {
response = target.queryParams(queryParams).request().get();
String jsonString = response.readEntity(String.class);
TypeFactory typeFactory = TypeFactory.defaultInstance();
List<E> list = this.mapper.readValue(
jsonString, typeFactory.constructCollectionType(ArrayList.class, resultClass));
return list;
} catch (Exception e) {
// Handle exception
} finally {
if (response != null)
response.close();
}
return null;
}
}
@ApplicationScoped
公共类RestHttpClient{
私人再融资客户;
@注入
私有对象映射器映射器;
@施工后
公共无效初始化(){
HttpParams params=新的BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(参数,5000);
HttpConnectionParams.setSoTimeout(参数,5000);
HttpClient HttpClient=新的默认HttpClient(参数);
this.client=new ResteasyClientBuilder().httpEngine(new ApacheHttpClient4Engine(httpClient)).build();
}
公共列表getList(最终类resultClass、最终字符串路径、,
多值映射查询参数){
ResteasyWebTarget=this.client.target(路径);
响应=空;
试一试{
response=target.queryParams(queryParams.request().get();
String jsonString=response.readEntity(String.class);
TypeFactory=TypeFactory.defaultInstance();
List List=this.mapper.readValue(
jsonString,typeFactory.constructionCollectionType(ArrayList.class,resultClass));
退货清单;
}捕获(例外e){
//处理异常
}最后{
if(响应!=null)
response.close();
}
返回null;
}
}
它很好用,但是。。。如果我快速连续多次调用getList()方法,有时会出现错误“BasicClientConnManager的使用无效:连接仍已分配”。我可以一次又一次地进行相同的调用序列,并且它至少90%的时间都有效,因此它似乎是一种竞争条件。我正在关闭finally块中的Response对象,这应该足以释放所有资源,但显然不是。我还需要做些什么来确保连接被释放?我在网上找到了一些答案,但它们要么太旧,要么不够具体。我使用的是resteasy client 3.0.4.Final。我猜您只有一个类RestHttpClient的实例,并且所有线程/请求都使用相同的对象 默认的ResteasyClientBuilder不使用连接池。这意味着一次只能有一个并行连接。在第二次使用ResteasyClient之前,需要返回一个请求(错误消息“connection still allocated”)。您可以通过增加连接池大小来避免这种情况:
ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder();
clientBuilder = clientBuilder.connectionPoolSize( 20 );
ResteasyWebTarget target = clientBuilder.build().target( "http://your.host" );
我正在使用以下RestClientFactory来设置一个新的客户端。它为您提供原始响应的调试输出,指定密钥库(客户端ssl证书所需)、连接池和使用代理的选项
public class RestClientFactory {
public static class Options {
private final String baseUri;
private final String proxyHostname;
private final String proxyPort;
private final String keystore;
private final String keystorePassword;
private final String connectionPoolSize;
private final String connectionTTL;
public Options(String baseUri, String proxyHostname, String proxyPort) {
this.baseUri = baseUri;
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.connectionPoolSize = "100";
this.connectionTTL = "500";
this.keystore = System.getProperty( "javax.net.ssl.keyStore" );
this.keystorePassword = System.getProperty( "javax.net.ssl.keyStorePassword" );
}
public Options(String baseUri, String proxyHostname, String proxyPort, String keystore, String keystorePassword, String connectionPoolSize, String connectionTTL) {
this.baseUri = baseUri;
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.connectionPoolSize = connectionPoolSize;
this.connectionTTL = connectionTTL;
this.keystore = keystore;
this.keystorePassword = keystorePassword;
}
}
private static Logger log = LoggerFactory.getLogger( RestClientFactory.class );
public static <T> T createClient(Options options, Class<T> proxyInterface) throws Exception {
log.info( "creating ClientBuilder using options {}", ReflectionToStringBuilder.toString( options ) );
ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder();
ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
RegisterBuiltin.register( providerFactory );
providerFactory.getClientReaderInterceptorRegistry().registerSingleton( new ReaderInterceptor() {
@Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
if (log.isDebugEnabled()) {
InputStream is = context.getInputStream();
String responseBody = IOUtils.toString( is );
log.debug( "received response:\n{}\n\n", responseBody );
context.setInputStream( new ByteArrayInputStream( responseBody.getBytes() ) );
}
return context.proceed();
}
} );
clientBuilder.providerFactory( providerFactory );
if (StringUtils.isNotBlank( options.proxyHostname ) && StringUtils.isNotBlank( options.proxyPort )) {
clientBuilder = clientBuilder.defaultProxy( options.proxyHostname, Integer.parseInt( options.proxyPort ) );
}
// why the fuck do you have to specify the keystore with RestEasy?
// not setting the keystore will result in not using the global one
if ((StringUtils.isNotBlank( options.keystore )) && (StringUtils.isNotBlank( options.keystorePassword ))) {
KeyStore ks;
ks = KeyStore.getInstance( KeyStore.getDefaultType() );
FileInputStream fis = new FileInputStream( options.keystore );
ks.load( fis, options.keystorePassword.toCharArray() );
fis.close();
clientBuilder = clientBuilder.keyStore( ks, options.keystorePassword );
// Not catching these exceptions on purpose
}
if (StringUtils.isNotBlank( options.connectionPoolSize )) {
clientBuilder = clientBuilder.connectionPoolSize( Integer.parseInt( options.connectionPoolSize ) );
}
if (StringUtils.isNotBlank( options.connectionTTL )) {
clientBuilder = clientBuilder.connectionTTL( Long.parseLong( options.connectionTTL ), TimeUnit.MILLISECONDS );
}
ResteasyWebTarget target = clientBuilder.build().target( options.baseUri );
return target.proxy( proxyInterface );
}
}
创建客户端:
SimpleClient client = RestClientFactory.createClient(
new RestClientFactory.Options(
"https://your.service.host",
"proxyhost",
"8080",
"keystore.jks",
"changeit",
"20",
"500"
)
,SimpleClient.class
);
另见:
请在新版本的Resteasy 3.6.x中引导?连接池是否已激活(50个连接)
SimpleClient client = RestClientFactory.createClient(
new RestClientFactory.Options(
"https://your.service.host",
"proxyhost",
"8080",
"keystore.jks",
"changeit",
"20",
"500"
)
,SimpleClient.class
);