This is the 7th day of my participation in the August More Text Challenge
Author: Tom Brother wechat official account: Micro technology
As a mainstream micro-service framework, Spring Boot has a mature community ecosystem. Market widely used, in order to facilitate everyone, organizing a spring-based boot used middleware introduction to quickly integrate series manual, involving the RPC, caching, message queues, depots table, registry, common open source components, such as distributed configuration about dozens of article, will open up in succession, the students are interested in, please attention & collection in advance
As a distributed cache component, Redis provides the cache between multiple services. However, Redis requires network overhead and increases time consumption. Local cache reads data directly from local memory, has no network overhead, and has higher performance, such as second kill system or cache with small data volume, than remote cache.
Caffeine is introduced
Caffeine is a high-performance cache library based on JAVA 8. Reference Google Guava API to rewrite the cache framework, based on LRU algorithm implementation, support a variety of cache expiration strategies.
The default local Cache in Spring Boot 1.x is the Guava Cache. After Spring5 (Spring Boot 2.x), Spring officially dropped The Guava Cache as a caching mechanism and used the better performance Caffeine as the default Cache component, which was a big boost for Caffeine.
Why does Spring do this? Caffeine contains impressive Benchmarks, which show that it performs very well compared to several other caching frameworks for both read and write scenarios.
Project integration
Add caffeine JAR package dependencies to pom.xml:
< the dependency > < groupId > com. Making. Ben - manes. Caffeine < / groupId > < artifactId > caffeine < / artifactId > < version > 2.8.6 < / version > </dependency>Copy the code
Initialize the Bean instance of the local cache class and set a series of configuration parameters to meet the requirements of personalized business scenarios.
public static LoadingCache<Long, User> loadingCache = Caffeine. NewBuilder () // Initial cache size. InitialCapacity (5) // Maximum number of cache entries. .expireAfterWrite(4, TimeUnit.SECONDS) .expireAfterAccess(10, TimeUnit.SECONDS) .refreshAfterWrite(6, Timeunit.seconds).recordStats().removalListener(new removalListener <Long, User>() { @Override public void onRemoval(@Nullable Long key, @Nullable User user, @nonnull RemovalCause RemovalCause) {system.out. printf("Key: %s, value: %s was removed! Cause (%s) \n", key, user, removalCause); }}).build(id -> {system.out.println (" cache not hit, from database, user id: "+ id); return User.builder().id(id).userName("Lily").age(new Random().nextInt(20)).build(); });Copy the code
Parameter Description:
-
InitialCapacity Initial cache capacity
-
MaximumSize Maximum number of cache entries
-
MaximumWeight indicates the maximumWeight of the cache
-
ExpireAfterAccess Expires at a fixed time after the last write or access
-
ExpireAfterWrite expireAfterWrite Expires at a fixed time after the last write
-
RefreshAfterWrite After a write is written, it expires within a fixed time. The next access returns the old value and triggers refreshing
-
WeakKeys Opens weak references to keys
-
WeakValues Turns on the weak reference of a value
-
SoftValues Enables the soft reference of value
-
RecordStats Cache usage statistics
If both expireAfterWrite and expireAfterAccess exist at the same time, expireAfterWrite prevails.
WeakValues and softValues cannot be used simultaneously.
MaximumSize and maximumWeight cannot be used together.
Construct the LoadingCache object, which provides a number of methods to manipulate the cache, such asgetIfPresent
、put
,invalidate
For details, please refer to the following figure:
How to demonstrate the use of Caffine Cache parameters and common apis in various scenarios
@RunWith(SpringRunner.class) @SpringBootTest(classes = StartApplication.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class CacheServiceTest { /** * .refreshAfterWrite(6,TimeUnit.SECONDS) * .build(id -> { * Thread.sleep(2_000); * system.out. println(" cache not hit, load from database, user ID: "+ id); * return User.builder().id(id).userName("Lily").age(new Random().nextInt(20)).build(); *}); */ @Test public void test1_refreshAfterWrite() throws ExecutionException, InterruptedException { User user1 = LocalCacheService.loadingCache.get(1L); System.out.println(" user1: "+ user1); Thread.sleep(10_000); / / the cache was not found, returns the old value, and triggers the cache refresh user1. = LocalCacheService loadingCache. Get (1 l); System.out.println(" user1: "+ user1); Thread.sleep(5_000); user1 = LocalCacheService.loadingCache.get(1L); System.out.println(" user1: "+ user1); } /** * .expireAfterWrite(4, TimeUnit.SECONDS) * .expireAfterAccess(10,TimeUnit.SECONDS) */ @Test public void test2() throws ExecutionException, InterruptedException { User user1 = LocalCacheService.loadingCache.get(1L); System.out.println(" user1: "+ user1); Thread.sleep(6_000); user1 = LocalCacheService.loadingCache.get(1L); System.out.println(" user1: "+ user1); } /** * The maximum capacity is 10, * maximumSize(10) */ @test public void test3() throws ExecutionException, InterruptedException { for (int i = 1; i < 16; i++) { User user1 = LocalCacheService.loadingCache.get(Long.valueOf(i)); Println (string. format(" user: %s", I, user1)); } Thread.sleep(30_000); }}Copy the code
Cache populating strategy
There are three ways to fill the cache: manual, synchronous, and asynchronous
1. Manual loading
Manually place values in the cache before retrieving them
cache.put(key, dataObject);
dataObject = cache.getIfPresent(key);
Copy the code
We can retrieve the value of a key using cache.getifPresent (key), and put the value into the cache as indicated by cache.put(key, value), but this overwrites the original key.
Cache.get (key, k – > value) is recommended. The get method passes Function (createExpensiveGraph) with key as an argument. If the key does not exist in the cache, the Function Function is called and the return value is inserted into the cache as the value of the cache. The GET method is executed blocking, and the Function method is called only once, even if multiple threads request the value at the same time. This avoids competing with writes from other threads, which is why get is better than getIfPresent.
2. Synchronous loading
This load cache approach uses a get method similar to the manual strategy used to initialize functions of values. Let’s see how to use it.
Caffeine.newBuilder().maximumsize (10).build(id -> {system.out.println (" cache not hit, load from database, user id: "+ id); return User.builder().id(id).userName("Lily").age(new Random().nextInt(20)).build(); });Copy the code
3. Asynchronous loading
This strategy is similar to synchronous loading, but performs the operation asynchronously and returns a CompletableFuture containing the value
AsyncLoadingCache<String, Object> asyncLoadingCache=Caffeine.newBuilder().maximumsize (10).buildAsync(id -> {system.out.println (" cache not hit, load from database, Userid: "+ id"; return User.builder().id(id).userName("Lily").age(new Random().nextInt(20)).build(); });Copy the code
Eviction strategies
Caffeine offers three types of expulsion strategies: size-based, time-based and reference-based.
1, size based (size-based)
There are two ways of size based expulsion: one is based on cache size, and the other is based on weights.
Use the Caffeine. MaximumSize (long) method to specify the maximumSize of the cache. When the cache exceeds this capacity, the Window TinyLfu policy is used to delete the cache.
You can also use a heavy policy for banding, using Caffeine. Weigher (weigher) to specify the weight and Caffeine. MaximumWeight (long) to specify the maximum cache weight.
MaximumWeight and maximumSize cannot be used together.
2. Time-based
Caffeine offers three timed expulsion strategies:
-
ExpireAfterAccess (long, TimeUnit) : Starts timing after the last access or write and expires after a specified time. If there are always requests to access the key, the cache will never expire.
-
ExpireAfterWrite (long, TimeUnit) : Starts a timer after the last write to the cache and expires after a specified time.
-
ExpireAfter (Expiry) : a custom policy. The Expiry time is calculated solely by the Expiry implementation.
3. Reference-based
We can configure caching expulsion based on the garbage collector. To do this, we can configure key and value as weak references or only values as soft references.
Note: AsyncLoadingCache does not support weak and soft references.
Caffeine. WeakKeys () uses weak references to store keys. If there is no strong reference to the key anywhere else, the cache is reclaimed by the garbage collector. Since the garbage collector only relies on identity equality, this causes the entire cache to compare keys using identity (==) equality instead of equals().
Caffeine. WeakValues () uses weak references to store values. If there is no strong reference to the value elsewhere, the cache is reclaimed by the garbage collector. Since the garbage collector only relies on identity equality, this causes the entire cache to compare keys using identity (==) equality instead of equals().
Caffeine.softvalues () stores values using soft references. When memory is full, soft-referenced objects are garbage collected in the least-recently-used manner. Since using soft references requires waiting until memory is full, it is generally recommended to set a maximum memory usage for the cache. SoftValues () will compare values using identity (==) instead of equals().
Note: Caffeine. WeakValues () and Caffeine. SoftValues () cannot be used together.
Manually deleting cache
At any time, you can actively invalidate the cache without waiting for it to be ejected
// invalidateAll(keys) // invalidateAll(keys) // invalidateAll(keys)Copy the code
statistical
Cache<String, Object> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
Copy the code
This can be converted to a collection of statistics by using Caffeine.recordStats(). Returns a CacheStats via cache.stats (). CacheStats provides the following statistical methods:
-
HitRate () : Returns the cache hit ratio
-
EvictionCount () : number of cache reclaims
-
AverageLoadPenalty () : Average time to load a new value
Demo code address
https://github.com/aalansehaiyang/spring-boot-bulking modules: spring - the boot - bulking - caffeineCopy the code
Author introduction: Tom brother, computer graduate student, the school recruited ali, P7 technical expert, has a patent, CSDN blog expert. Responsible for e-commerce transactions, community fresh, flow marketing, Internet finance and other businesses, many years of first-line team management experience