Cache HTTP Response in Spring MVC Rest service - spring

I have a spring MVC rest service that returns data in XML. I would like to cache this xml response. How can I achieve this? Is it possible to do this using mvc:interceptors?

You could make this work, but I think there are better solutions.
First, if you want to use Spring MVC interceptors, you'll use the postHandle method to store something in your cache and the preHandle to check the cache and possible circumvent processing. The question is, what do you store in the cache. You would need to store the complete response. This means that you would have to easily get the full response from your ModelAndView in postHandle. This may or may not be easy, depending on how you're doing things.
You're most likely better off using a different caching mechanism all together. I recommend caching at the web server level. This is especially true if you're looking to cache in the interceptor level as that is right "next" to the web server and I don't see any benefit in re-inventing the wheel there. Apache has a cache module. So does nginx. Varnish is pretty awesome too.
I should also mention that you should not cache until you've determined that you need to (don't prematurely optimize). This is a waste of your time and effort. Secondly, when you've determined that you do have performance issues that need to be fixed (and caching is the correct solution), you should cache the right data in the right place.
Now, say you've determined that you do have a performance problem and some sort of caching is a good solution. The next thing to determine is what can be cached. If, for every URL, you return the same data, then caching at the web server (Apache, nginx, Varnish, etc.) level will be your best bet.
Often, you will have cases where two clients will hit the same URL and get different data. This is most easily seen on a site like Facebook. I see different data when I'm logged in than my friend sees. In this case, you will not be able to cache at the web server level. You will need to cache inside your application. Usually this means caching at the database level.

I couldn't disagree with the optimization part of the solution more.
Web requests are inherently slow as you're loading data from a remote location, possibly a few thousand miles away. Each call must suffer a full TCP round-trip time for at least the packets themselves, possibly the connect and fin for each request, which for connect is a three packet synchronous exchange before you start to transfer data.
US coast-to-coast latency is about 50ms on a good day, so every connection suffers a 150ms penalty, which for most implementations is incurred for every request.
Caching the response on the client-side removes this latency entirely, and if the service has correct headers on their response, is trivial. If they don't, you'll have to define a caching policy, which for the most part isn't particularly difficult. Most API calls are either real-time or not.
In my opinion, caching REST responses isn't premature optimization, it's common sense.

Don't use spring cache it is not what you need. You need to reduce load to your Server, not speed up your inner spring application execution.
Try use som HTTP-related caching strategies.
You can add one of HTTP-headers to your requests
#cache expires in 3600 seconds
cache-control: private, max-age=3600
#hash of your content
ETag: "e6811cdbcedf972c5e8105a89f637d39-gzip"
# redirect caching to any HTTP header
vary: User-Agent
Detailed description of caching techniques
Spring example
#RequestMapping (value = "/resource/1.pdf", produces = "application/octet-stream")
public ResponseEntity<InputStreamResource> getAttachement (#RequestParam (value = "id") Long fileId)
{
InputStreamResource isr = new InputStreamResource(javaInputStream);
HttpHeaders headers = new HttpHeaders();
//other headers
headers.setCacheControl("private, max-age=3600");
return new ResponseEntity<>(irs, headers, HttpStatus.OK);
}

I use this and it works with awesome speed.
Really easy to use spring + ehcache:
1)Controller:
#Cacheable("my.json")
#RequestMapping("/rest/list.json")
public ResponseEntity list(#RequestParam(value = "page", defaultValue = "0", required = false)
int pageNum,
#RequestParam(value = "search", required = false)
String search) throws IOException {
...
}
2) At ehcache.xml some like this:
<cache name="my.json" maxElementsInMemory="10000" eternal="true" overflowToDisk="false"/>
3) Configure spring. I'm using spring javaconf style:
#Configuration
#EnableCaching
public class ApplicationConfiguration {
#Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() throws MalformedURLException {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
#Bean
#Autowired
public EhCacheCacheManager cacheManager(EhCacheManagerFactoryBean ehcache) {
EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
ehCacheCacheManager.setCacheManager(ehcache.getObject());
return ehCacheCacheManager;
}
}

At the application level, I would go with a plain Java cache as EHCache. EHCache is pretty easy to integrate with methods on Spring beans. You could annotate your service methods as #Cacheable and it's done. Check it out at EHCache Spring Annotations.
At the HTTP level, Spring MVC provides a useful ETag filter. But I think it would be better if you could configure this kind of caching at the server level more than at app level.

As of Spring 3.1, you can use the #Cachable annotation. There is also support for conditional caching, and some sibling annotations like #CachePut, #CacheEvict and #Caching for more fine grained control.
Spring currently supports two different cache managers, one that is backed by a ConcurrentHashMap and one that is backed by Ehcache.
Lastly, don't forget to read the details about how to enable the annotations.

Related

Spring Boot Caching auto refresh using #PostConstruct

I currently have a Spring Boot based application where there is no active cache. Our application is heavily dependent on key-value configurations which we maintain in an Oracle DB. Currently, without cache, each time I want to get any value from that table, it is a database call. This is, expectedly causing a lot of overhead due to high number of transactions to the DB. Hence, the need for cache arrived.
On searching for caching solutions for SpringBoot, I mostly found links where we are caching object while any CRUD operation is performed via the application code itself, using annotations like #Cacheable, #CachePut, #CacheEvict, etc. but this is not applicable for me. I have a master data of key-value pairs in the DB, any change needs approvals and hence the access is not directly provided to the user, it is made once approved directly in the DB.
I want to have these said key-values to be loaded at startup time and kept in the memory, so I tried to implement the same using #PostConstruct and ConcurrentHashMap class, something like this:
public ConcurrentHashMap<String, String> cacheMap = new ConcurrentHashMap<>();
#PostConstruct
public void initialiseCacheMap() {
List<MyEntity> list = myRepository.findAll();
for(int i = 0; i < list.size(); i++) {
cacheMap.put(list.get(i).getKey(), list.get(i).getValue());
}
}
In my service class, whenever I want to get something, I am first checking if the data is available in the map, if not I am checking the DB.
My purpose is getting fulfilled and I am able to drastically improve the performance of the application. A certain set of transactions were earlier taking 6.28 seconds to complete, which are now completed in mere 562 milliseconds! however, there is just one problem which I am not able to figure out:
#PostConstruct is called once by Spring, on startup, post dependency injection. Which means, I have no means to re-trigger the cache build without restart or application downtime, this is not acceptable unfortunately. Further, as of now, I do not have the liberty to use any existing caching frameworks or libraries like ehcache or Redis.
How can I achieve periodic refreshing of this cache (let's say every 30 minutes?) with only plain old Java/Spring classes/libraries?
Thanks in advance for any ideas!
You can do this several ways, but how you can also achieve this is by doing something in the direction of:
private const val everyThrityMinute = "0 0/30 * * * ?"
#Component
class TheAmazingPreloader {
#Scheduled(cron = everyThrityMinute)
#EventListener(ApplicationReadyEvent::class)
fun refreshCachedEntries() {
// the preloading happens here
}
}
Then you have the preloading bits when the application has started, and also the refreshing mechanism in place that triggers, say, every 30 minutes.
You will require to add the annotation on some #Configuration-class or the #SpringBootApplication-class:
#EnableScheduling

coordinating multiple outgoing requests in a reactive manner

this is more of a best practice question.
in my current system (monolith), a single incoming http api request might need to gather similarly structured data from to several backend sources, aggregate it and only then return the data to the client in the reponse of the API.
in the current implementation I simply use a threadpool to send all requests to the backend sources in parallel and a countdown latch of sorts to know all requests returned.
i am trying to figure out the best practice for transforming the described above using reactice stacks like vert.x/quarkus. i want to keep the reactiveness of the service that accepts this api call, calls multiple (similar) backend source via http, aggregates the data.
I can roughly guess I can use things like rest-easy reactive for the incoming request and maybe MP HTTP client for the backend requests (not sure its its reactive) but I am not sure what can replace my thread pool to execute things in parallel and whats the best way to aggregate the data that returns.
I assume that using a http reactive client I can invoke all the backend sources in a loop and because its reactive it will 'feel' like parralel work. and maybe the returned data should be aggragated via the stream API (to join streams of data)? but TBH I am not sure.
I know its a long long question but some pointers would be great.
thanks!
You can drop the thread pool, you don't need it to invoke your backend services in parallel.
Yes, the MP RestClient is reactive. Let's say you have this service which invokes a backend to get a comic villain:
#RegisterRestClient(configKey = "villain-service")
public interface VillainService {
#GET
#Path("/")
#NonBlocking
#CircuitBreaker
Uni<Villain> getVillain();
}
And a similar one for heroes, HeroService. You can inject them in your endpoint class, retrieve a villain and a hero, and then compute the fight:
#Path("/api")
public class Api {
#RestClient
VillainService villains;
#RestClient
HeroService heroes;
#Inject
FightService fights;
#GET
public Uni<Fight> fight() {
Uni<Villain> villain = villains.getVillain();
Uni<Hero> hero = heroes.getRandomHero();
return Uni.combine().all().unis(hero, villain).asTuple()
.chain(tuple -> {
Hero h = tuple.getItem1();
Villain v = tuple.getItem2();
return fights.computeResult(h, v);
});
}
}

Spring Cache Hit Flag/Indicator

I am using Spring Cache to cache some objects through #Cacheable. However, 1 of the requirement requires me to be able to know if the returned object was from the Cache Hit or standard call. Is there any flag or indicator thats gets set i can use to check this ?
I have seen past questions regarding cache hits being logged whenever there are cache hits but that is not really useful for my situation. I am currently using Spring Cache with the Simple Provider and am open to using any external Cache Managers that is able to do this.
Yes, we can know whether it is a cache hit or a cache miss(a direct call to REST call or a database call) using a flag.
Using #Cacheable, it always first checks in the cache, before it executes the method, if found in cache, it will skip the method execution, where as #CachePut works slightly different, where it will executes the adivised method & updates the cache, so it will miss the cache always.
For example:
private volatile boolean cacheMiss = false;
public boolean isCacheMiss(){
boolean cacheMiss = this.cacheMiss;
this.cacheMiss = false; //resetting for next read
return cacheMiss;
}
protected void setCacheMiss(){
this.cacheMiss = true;
}
#Cacheable("Quotes")
public Quote requestQuote(Long id) {
setCacheMiss();
//REST CALL HERE
return requestQuote(ID_BASED_QUOTE_SERVICE_URL,
Collections.singletonMap("id", id));
}
cacheMiss variable gives the status, whether it is from cache or not.
Here is it discussed Spring Caching with GemFire, the underlying caching provider is Pivotal GemFire. You can use any such caching providers.

Elasticsearch upgrade path from transport client to high level REST client

What is the upgrade path for an application using the Elasticsearch native Java client API (TransportClient) to move to using the high-level REST client for Java?
Documentation (preliminary?) seems to indicate:
The Java High Level REST Client depends on the Elasticsearch core
project. It accepts the same request arguments as the TransportClient
and returns the same response objects.
(Source: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/5.x/java-rest-high.html)
But I am not entirely clear what this means. Will I be able to switch my entire codebase over to the high level REST client without rewriting my queries, or other client-type operations? It doesn't seem like the REST client implements the Client interface. That may make sense from a decoupling point-of-view.'
What I need to know is whether I should be building my own abstraction around client operations, or whether HighLevelRestClient will be basically implementing the Client interface already.
Should I continue, for the time being, to write code against the TransportClient API or will that code all need to be rewritten when TransportClient is deprecated?
Note that I am looking at the high-level REST client, not the low-level REST client.
The high level REST client doesn't implement the Client interface. The plan is described in this blogpost that I wrote a while ago.
We are also in the process of writing documentation, which will contain a page with instructions on how to migrate from the transport client.
The new client reuses requests and responses from the existing transport client, but the client object is not compatible, that means that for instance the following:
IndexRequest indexRequest = new IndexRequest("index", "type", "id");
indexRequest.source("field", "value");
IndexResponse indexResponse = transportClient.index(indexRequest).get();
will become something like:
IndexRequest indexRequest = new IndexRequest("index", "type", "id");
indexRequest.source("field", "value");
IndexResponse indexResponse = restHighLevelClient.index(indexRequest);
As for async requests, the call is slightly different (see the method name), in the new client we went for a different method with a name that ends with the "Async" suffix, you would go from the following:
transportClient.index(indexRequest, new ActionListener<IndexResponse>() {
#Override
public void onResponse(IndexResponse indexResponse) {
// called when the operation is successfully completed
}
#Override
public void onFailure(Exception e) {
// called on failure
}
});
to the following:
restHighLevelClient.indexAsync(indexRequest, new ActionListener<IndexResponse>() {
#Override
public void onResponse(IndexResponse indexResponse) {
// called when the operation is successfully completed
}
#Override
public void onFailure(Exception e) {
// called on failure
}
});
Unfortunately the Client#prepare* methods won't be available in the high level client, so something like:
IndexResponse indexResponse = transportClient.prepareIndex("index", "type", "id").setSource("field", "value").get();
needs to be migrated to the above using ActionRequests rather then ActionRequestBuilders. We are making this change as there was always confusion between requests and builders in the transport client, two ways of doing exactly the same thing. The new client will have a single way to provide requests.
If you want to have a look at the current documentation, it is already live although work in progress: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-high.html .
The High Level REST Client will replace the Transport Client, although its first upcoming release will only support index, bulk, get, delete, update, search, search scroll and clear scroll APIs. Support for missing APIs will come next, we are also open to contributions from users as usual.
The Transport Client will be soon deprecated, hence I would advice to move over the the High Level REST Client as soon as possible, it shouldn't be a huge change and it will pay off as we will be improving it overtime, already going through REST is a great improvement.

Heavy REST Application

I have an Enterprise Service Bus (ESB) that posts Data to Microservices (MCS) via Rest. I use Spring to do this. The main Problem is that i have 6 Microservices, that run one after one. So it looks like this: MCS1 -> ESB -> MCS2 -> ESB -> ... -> MCS6
So my Problem looks like this: (ESB)
#RequestMapping(value = "/rawdataservice/container", method = RequestMethod.POST)
#Produces(MediaType.APPLICATION_JSON)
public void rawContainer(#RequestBody Container c)
{
// Here i want to do something to directly send a response and afterwards execute the
// heavy code
// In the heavy code is a postForObject to the next Microservice
}
And the Service does something like this:
#RequestMapping(value = "/container", method = RequestMethod.POST)
public void addDomain(#RequestBody Container container)
{
heavyCode();
RestTemplate rt = new RestTemplate();
rt.postForObject("http://134.61.64.201:8080/rest/rawdataservice/container",container, Container.class);
}
But i dont know how to do this. I looked up the post for Location method, but i dont think it would solve the Problem.
EDIT:
I have a chain of Microservices. The first Microservice waits for a Response of the ESB. In the response the ESB posts to another Microservice and waits for a response and the next one does the same as the first one. So the Problem is that the first Microservice is blocked as long as the complete Microservice Route is completed.
ESB Route
Maybe a picture could help. 1.rawdataService 2.metadataservice 3.syntaxservice 4.semantik
// Here i want to do something to directly send a response and afterwards execute the
// heavy code
The usual spelling of that is to use the data from the http request to create a Runnable that knows how to do the work, and dispatch that runnable to an executor service for later processing. Much the same, you copy the data you need into a queue, which is polled by other threads ready to complete the work.
The http request handler then returns as soon as the executor service/queue has accepted the pending work. The most common implementation is to return a "202 Accepted" response, including in the Location header the url for a resource that will allow the client to monitor the work in progress, if desired.
In Spring, it might be ResponseEntity that manages the codes for you. For instance
ResponseEntity.accepted()....
See also:
How to respond with HTTP 400 error in a Spring MVC #ResponseBody method returning String?
REST - Returning Created Object with Spring MVC
From the caller's point of view, it would invoke RestTemplate.postForLocation, receive a URI, and throw away that URI because the microservice only needs to know that the work as been accepted
Side note: in the long term, you are probably going to want to be able to correlate the activities of the different micro services, especially when you are troubleshooting. So make sure you understand what Gregor Hohpe has to say about correlation identifiers.

Resources