Cache HTTP Response in Spring MVC Rest service

2019-06-18 16:10发布

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?

6条回答
劳资没心,怎么记你
2楼-- · 2019-06-18 16:17

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);

}
查看更多
仙女界的扛把子
3楼-- · 2019-06-18 16:21

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;
    }
}
查看更多
我只想做你的唯一
4楼-- · 2019-06-18 16:22

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.

查看更多
聊天终结者
5楼-- · 2019-06-18 16:27

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.

查看更多
欢心
6楼-- · 2019-06-18 16:30

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.

查看更多
我只想做你的唯一
7楼-- · 2019-06-18 16:40

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.

查看更多
登录 后发表回答