How to integrate Spring Cache with an In-Memory Data Grid (IMDG)
Interested in combining the efficiencies of Spring Cache with the power of an In-Memory Data Grid (IMDG). This simple tutorial demonstrates just how easy the process is.
The non-functional requirement that plagues just about every enterprise software application is performance. From slow response times frustrating users, to blown budget caused by the standard approach of throwing more hardware at the problem, just about every stakeholder in the problem domain has a vested interest in making applications run as fast and efficiently as possible.
Updated inversion of control (IoC) tutorials and dependency injection examples
TheServerSide has been updating its resources in terms of the Spring framework, Java EE contexts and various different approaches to inversion of control (IoC) and dependency injection (DI). Check out these newer resources on the topic:
- Inversion of control explained fast with a Spring IoC example
- Spring MVC Java web development with Spring Boot
- Drawbacks to IoC and how to solve inversion of control problems
- Spring IoC vs. Google Guice's inversion of control approach
- Getting started with Google Guice
- Getting started with the Spring IoC container
- The beauty of Spring without XML: Annotation based IoC
The most common design pattern used to address the performance problem is caching. Caching of methods helps to reduce the number of executions of any method for a given set of parameters saving precious CPU load or expensive database trips. The more data you can cache, the faster your applications will perform. And one of the most effective types of caches you can use is an in-memory data grid. An in memory data grid stores everything in memory, making data access fast and efficient. The data is distributed amongst many servers, thus the term grid in the name. And additional servers providing additional memory can be swapped in and out on the fly, allowing for a highly elastic approach to cache management.
So how do you incorporate a IMDG cache into your applications? To instruct your program to use a cache, a few lines of code are required. Java has standardized caching with JSR 107, the Java Caching API, but Spring's Cache API remains the most popular. Spring is one of the most popular frameworks in the Java industry. It is a lightweight dependency injection container and it helps you to reduce the overall complexity of an enterprise application. This helps you to write modular code which makes applications easier to manage. Such benefits make Spring Framework an essential for many high traffic enterprise applications.
Integrating Spring Cache into your code
We'll take a look here at how to sprinkle a little bit of Sping code onto a Java class to enable caching. There's a bit of code here, but really, the only part you really need to be concerned with is the addition of the @Cachable annotation on the getId method:
//POJO public class Customer implements Serializable{ private String customerId; private String customerName; public Customer(String customerId, String customerName) { this.customerId = customerId; this.customerName = customerName; } public String getCustomerId() { return customerId; } public void setCustomerId(String customerId) { this.customerId = customerId; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public String toString() { return "Customer{" + "customerId='" + customerId + '\'' + ", customerName='" + customerName + '\'' + '}'; } } package com.alachisoft; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class SimpleCustomerRepository implements CustomerRepository { private static final Logger log = LoggerFactory.getLogger(SimpleCustomerRepository.class); @Override //@Cacheable("Customer") public Customer getById(String customerId) { simulateSlowService(); return new Customer(customerId, "John Doe"); } //expensive database call or computation private void simulateSlowService() { try { log.info("Simulating slow service"); long time = 5000L; Thread.sleep(time); } catch (InterruptedException e) { throw new IllegalStateException(e); } } }
By simply adding the @Cacheable annotation to the method getById, the Spring framework intercepts all subsequent calls. Spring then looks in the cache to see if the method has been executed with the provided parameters. If it has then the method result is fetched from the cache and returned. If not then the method executes as is and then the result is populated into the cache for future references. This avoids going through the same computation repetitively and eliminates superfluous database trips. Now consider the following application making use of SimpleBookRepository.
package com.alachisoft; import com.alachisoft.tayzgrid.integrations.spring.TayzGridCacheManager; import com.alachisoft.tayzgrid.integrations.spring.configuration.TayzGridConfigurationManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Component; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.cache.CacheManager; @SpringBootApplication @EnableCaching public class Main{ private static final Logger log = LoggerFactory.getLogger(Main.class); @Component static class Runner implements CommandLineRunner { @Autowired private CustomerRepository customerRepository; @Override public void run(String... args) throws Exception { log.info("... Fetching customer"); log.info("customer_1234 -->" + customerRepository.getById("customer_1234")); log.info("customer_1234 -->" + customerRepository.getById("customer_1234")); log.info("customer_1234 -->" + customerRepository.getById("customer_1234")); } } @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("customer"); } public static void main(String[] args) { SpringApplication.run(Main.class, args); } }
Integrating the in-memory data grid
Just like other services in the Spring Framework, this caching service is an abstraction (not a cache implementation) and requires the use of an actual storage implementation to cache the data - that is, the abstraction frees the developer from having to write the caching logic but does not provide the actual store.
The main advantage of keeping this modular is that you can change the underlying caching solution whenever you want to without the need of any code change.
To integrate a cache store the most common road which developers tend to take is to either implement a local cache or use a third party solution but these high traffic applications face a major scalability problem. Foreseeing the problem that you can always add more servers to your application tier but the databases cannot scale in the same fashion failing to serve peak loads. In such situations an In-Memory Data Grid is your best bet.
An in-Memory Data Grid (IMDG) is a key value store, distributed on several servers and keeps all the data in RAM to provide the best performance. Servers can be added and removed at runtime in an IMDG in a production environment to handle higher transaction loads and reduce the operational cost by removing servers when not required. Spring cache framework has a flexible architecture that allows third party In-Memory Data Grids to be plugged-in without any code changes to your application.
TayzGrid is one such In-Memory Data Grid and is extremely fast and scalable. It provides integration with Spring Cache to let you store your POJO in the data grid and boost your application performance and scalability.
You only need to add a different cache manager and add all the Tayzgrid references from the lib folder from where you installed TayzGrid. Just change the following function
@Bean public CacheManager cacheManager() { TayzGridConfigurationManager tgconfig = new TayzGridConfigurationManager(); tgconfig.setConfigFile("./tayzgrid-spring.xml"); TayzGridCacheManager cacheManager = new TayzGridCacheManager(); cacheManager.setTayzGridConfigurationManager(tgconfig); return cacheManager; }
Now create an XML file named tayzgrid-spring.xml and enter the following information.
...
Sping Cache helps in caching your methods and avoiding expensive database trips, all with a few configuration changes. Integrating an IMDG such as TayzGrid helps you scale your architecture at peak loads. Your application cluster will have access to distributed data thus providing high availability and scalability.
What types of approaches do you take to improve software performance? Let us know.