Quarkus, Hanging MP REST Client and the Solution 📎
@RegisterRestClient(baseUri = "http://localhost:8000")
public interface WebsiteClient {
@GET
@Produces(MediaType.TEXT_PLAIN)
Response getContent();
}
...injected to Quarkus JAX-RS resource:
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.eclipse.microprofile.rest.client.inject.RestClient;
@Path("website")
public class WebsiteResource {
@Inject
@RestClient
WebsiteClient firstClient;
@Inject
@RestClient
WebsiteClient secondClient;
@GET
public String injected() {
for (int i = 0; i < 500; i++) {
Response firstResponse = this.firstClient.getContent();
Response secondResponse = this.secondClient.getContent();
System.out.println(i);
}
return "+";
}
}
will block after 50 iterations.
Reason: the exhaustion of the underlying HTTP connection pool. See stack trace:
org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:393)
org.apache.http.pool.AbstractConnPool.access$300(AbstractConnPool.java:70)
org.apache.http.pool.AbstractConnPool$2.get(AbstractConnPool.java:253)
org.apache.http.pool.AbstractConnPool$2.get(AbstractConnPool.java:198)
org.apache.http.impl.conn.PoolingHttpClientConnectionManager.leaseConnection(PoolingHttpClientConnectionManager.java:306)
org.apache.http.impl.conn.PoolingHttpClientConnectionManager$1.get(PoolingHttpClientConnectionManager.java:282)
org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:190)
org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.invoke(ManualClosingApacheHttpClient43Engine.java:302)
The underlying resources are freed with the Response#close call, now all 500 iterations are performed without blocking:
@GET
public String injected() {
for (int i = 0; i < 500; i++) {
Response firstResponse = this.firstClient.getContent();
firstResponse.close();
Response secondResponse = this.secondClient.getContent();
firstResponse.close();
System.out.println(i);
}
return "+";
}
The blocking behaviour depends on the JAX-RS / MicroProfile Rest Client implementation. RESTEasy requires an explicit Response#close
call, Jersey does not.