1

I tried to find out how to config. a server-side rest client (i.e. microservice A calls other microservice B using rest) to used a http cache.

The background is, that the binary entities transfered over the wire can be quite large. Overall performance can benefit from a cache on microservice A side which employs http caching headers and etags provided by microservice B.

I found a solution that seems to work, but I'm not sure it that is a proper solution, that work together with current requests, that can occur on microservice A at any time.

    @Inject
    /* package private */  ManagedExecutor executor;

    //
    // Instead of using a declarative rest client we create it ourselves, because we can then supply a server-side cache: See ctor()
    //
    private ServiceBApi serviceClientB;

    @ConfigProperty(name="serviceB.url")
    /* package private */ String serviceBUrl;

    @ConfigProperty(name="cache-entries")
    /* package private */ int cacheEntries;

    @ConfigProperty(name="cache-entrysize")
    /* package private */ int cacheEntrySize;

    @PostConstruct
    public void ctor()
    {
        // Create proxy ourselves, because we can then supply a server-side cache
        final CacheConfig cc = CacheConfig.custom()
            .setMaxCacheEntries(cacheEntries)
            .setMaxObjectSize(cacheEntrySize)
            .build();
        final CloseableHttpClient httpClient = CachingHttpClientBuilder.create()
            .setCacheConfig(cc)
            .build();
        final ResteasyClient client = new ResteasyClientBuilderImpl()
            .httpEngine(new ApacheHttpClient43Engine(httpClient))
            .executorService(executor)
            .build();
        final ResteasyWebTarget target = (ResteasyWebTarget) client.target(serviceBUrl);
        this.serviceClientB = target.proxy(ServiceBApi.class);
    }

    @Override
    public byte[] getDoc(final String id)
    {
        try (final Response response = serviceClientB.getDoc(id)) {
            [...]
            // Use normally and no need to handle conditional gets and caching headers and other HTTP protocol stuff here, because this does underlying impl.
            [...]
        }
    }

My questions are:

  • Is my solution ok as server-side solution, i.e. can it handle concurrent requests?
  • Is there a declarative (quarkus) way (@RegisterRestClient. etc) to achieve the same?

-- Edit

To make things clear: I want service B to be able to control the caching based on the HTTP get request and the specific resource. Additionally I want to avoid the unnecessary transmission of the large documents service B provides.

-- Mik

Mik86
  • 161
  • 1
  • 6

1 Answers1

0

Assuming that you have worked with the declarative way of using Quarkus' REST Client before, you would just inject the client in your serviceB-consuming class. The method, that will invoke Service B, should be annotated with @CacheResult. This will cache results depending on the incoming id. See also Quarkus Cache Guide.

Please note: As Quarkus and Vert.x are all about non-blocking operations, you should use the async support of the REST Client.


    @Inject
    @RestClient
    ServiceBApi serviceB;

    ...
    @Override
    @CacheResult(cacheName = "service-b-cache")
    public Uni<byte[]> getDoc(final String id) {
        return serviceB.getDoc(id).map(...); 
    }
    ...
Oliver Marienfeld
  • 1,154
  • 9
  • 17
  • Yes, I read the caching guide before I created the initial post. The caching API, is a different usecase, than I have. I want to employ the HTTP builtin caching, which means caching-headers and etags, because I want the microservice B to control caching an transmissinon. – Mik86 Mar 02 '21 at 08:46
  • Hmm, now I understand! RESTEasy has an own solution for this, but it looks quite similar to your one: https://docs.jboss.org/resteasy/docs/4.6.0.Final/userguide/html_single/index.html#client_cache – Oliver Marienfeld Mar 03 '21 at 16:32