adam bien's blog

Quarkus, Hanging MP REST Client and the Solution 📎

The following (Rest Client for MicroProfile) interface:

@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.