This column will start from the basics, step by step, take actual combat as a clue, and gradually deepen the knowledge of SpringBoot related knowledge, create a complete SpringBoot learning process, improve engineering coding ability and thinking ability, and write high-quality code. I hope everyone can gain something from it, and please support me a lot.
Column address: SpringBoot Column
The code involved in this article has been placed on gitee: gitee address
If there are mistakes in the knowledge points of the article, please correct me! Everyone learns and progresses together.
Column summary: Column summary
SpringBoot integrates jetcache caching
At present, the caches we use are either A or B. Can A and B be used together? This section solves this problem. The integration of springboot for cache only stays on the use of cache. If the cache itself does not support the use of AB at the same time, springboot can't do it. Therefore, in order to solve the problem of using AB cache together, you must find a cache that can support both AB and AB. Cache is used together, is there such a cache? There really is, Ali produced, jetcache.
Strictly speaking, jetcache is not a caching solution. It can only be said that it is a caching framework, and then other caches are managed in jetcache, so that AB caches can be used together. And jetcache refers to springboot's idea of integrating caching, and the overall technology usage is very similar to springboot's caching solution idea. Let's use jetcache first, and then talk about some small functions in it.
Before doing it, make it clear that jetcache can’t just take two caches together. Currently, jetcache supports two types of caching schemes: local caching and remote caching, which are as follows:
- Local cache (Local)
- LinkedHashMap
- Caffeine
- Remote cache (Remote)
- Redis
- Tair
Why does jetcache only support 4 caches like 2+2? In fact, Ali developed this technology mainly to meet its own needs. There must be only 1+1 species at first, and gradually changed to 2+2 species. The following will use the LinkedHashMap+Redis scheme to realize the simultaneous use of local and remote caching schemes.
Pure remote solution
Step 1: Import springboot to integrate the coordinate starter corresponding to jetcache. The default remote scheme used by the current coordinate is redis
<dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>2.6.2</version> </dependency>
Step 2: Basic configuration of remote solution
jetcache: remote: default: type: redis host: localhost port: 6379 # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50
Where poolConfig is a required item, otherwise an error will be reported
Step 3: Enable caching, mark the annotation @EnableCreateCacheAnnotation above the boot class to configure the springboot program to create a cache in the form of annotations
@SpringBootApplication //jetcache main switch to enable caching @EnableCreateCacheAnnotation public class Springboot20JetCacheApplication { public static void main(String[] args) { SpringApplication.run(Springboot20JetCacheApplication.class, args); } }
Step 4: Create the cache object Cache, and use the annotation @CreateCache to mark the information of the current cache, and then use the API of the Cache object to operate the cache, put the write cache, and get the read cache.
@Service public class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils; @CreateCache(name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS) private Cache<String ,String> jetCache; public String sendCodeToSMS(String tele) { String code = codeUtils.generator(tele); jetCache.put(tele,code); return code; } public boolean checkCode(SMSCode smsCode) { String code = jetCache.get(smsCode.getTele()); return smsCode.getCode().equals(code); } }
Through the above jetcache using the remote scheme to connect to redis, it can be seen that the interface operation of jetcache when operating the cache is more in line with the developer's habits. When using the cache, the cache object Cache is obtained first, the data is put in, and the data is retrieved. get, which is simpler and easier Understand. And when jetcache operates the cache, you can set the expiration time for a cache object, and put the same type of data into the cache to facilitate the management of the effective period.
The above scheme uses the default cache defined in the configuration. In fact, this default is a name, which can be written or added at will. For example, to add another caching solution, refer to the following configuration:
jetcache: remote: default: type: redis host: localhost port: 6379 # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50 sms: type: redis host: localhost port: 6379 # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50
If you want to use the cache with the name sms, you need to specify the parameter area when creating the cache, and declare the corresponding cache.
@Service public class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils; @CreateCache(area="sms",name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS) private Cache<String ,String> jetCache; public String sendCodeToSMS(String tele) { String code = codeUtils.generator(tele); jetCache.put(tele,code); return code; } public boolean checkCode(SMSCode smsCode) { String code = jetCache.get(smsCode.getTele()); return smsCode.getCode().equals(code); } }
pure local solution
In the remote solution, remote is used in the configuration to indicate remote, and local is local, but the type is different.
Step 1: Import springboot to integrate the coordinate starter corresponding to jetcache
<dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>2.6.2</version> </dependency>
Step 2: Local cache basic configuration
jetcache: local: default: type: linkedhashmap keyConvertor: fastjson
In order to speed up the key matching speed during data acquisition, jetcache requires the type converter to specify the key. Simply put, if you give an Object as a key, I first convert it to a string using the key's type converter, and then save it. When the data is obtained, the given Object is still converted into a string first, and then matched according to the string. Since jetcache is Ali's technology, it is recommended to use Ali's fastjson as the key type converter here.
Step ③: Enable caching
@SpringBootApplication //jetcache main switch to enable caching @EnableCreateCacheAnnotation public class Springboot20JetCacheApplication { public static void main(String[] args) { SpringApplication.run(Springboot20JetCacheApplication.class, args); } }
Step 4: When creating the cache object Cache, mark the current use of the local cache
@Service public class SMSCodeServiceImpl implements SMSCodeService { @CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL) private Cache<String ,String> jetCache; public String sendCodeToSMS(String tele) { String code = codeUtils.generator(tele); jetCache.put(tele,code); return code; } public boolean checkCode(SMSCode smsCode) { String code = jetCache.get(smsCode.getTele()); return smsCode.getCode().equals(code); } }
cacheType controls whether the current cache uses the local cache or the remote cache. Configure cacheType=CacheType.LOCAL to use the local cache.
Local + Remote Scenario
Both local and remote methods are available. How to configure the two schemes together? In fact, it is enough to combine the two configurations.
jetcache: local: default: type: linkedhashmap keyConvertor: fastjson remote: default: type: redis host: localhost port: 6379 # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50 sms: type: redis host: localhost port: 6379 # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50
When creating a cache, configure the cacheType as BOTH, that is, the local cache and the remote cache are used at the same time.
@Service public class SMSCodeServiceImpl implements SMSCodeService { @CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.BOTH) private Cache<String ,String> jetCache; }
If cacheType is not configured, the default value is REMOTE, that is, only the remote cache scheme is used. For jetcache configuration, refer to the following information
Attributes | Defaults | illustrate |
---|---|---|
jetcache.statIntervalMinutes | 0 | Statistics interval, 0 means no statistics |
jetcache.hiddenPackages | none | When the name is automatically generated, hide the specified package name prefix |
jetcache.[local|remote].${area}.type | none | Cache type, local support linkedhashmap, caffeine, remote support redis, tair |
jetcache.[local|remote].${area}.keyConvertor | none | key converter, currently only supports fastjson |
jetcache.[local|remote].${area}.valueEncoder | java | Only remote type cache needs to be specified, optional java and kryo |
jetcache.[local|remote].${area}.valueDecoder | java | Only remote type cache needs to be specified, optional java and kryo |
jetcache.[local|remote].${area}.limit | 100 | Only the local type of cache needs to be specified, the maximum number of elements of the cache instance |
jetcache.[local|remote].${area}.expireAfterWriteInMillis | gigantic | Default expiration time, in milliseconds |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | Only local type caches are valid, milliseconds, maximum inactivity interval |
The above solutions only support manual control of the cache, but the method cache in the springcache solution is particularly useful. Add an annotation to a method and the method will automatically use the cache. jetcache also provides a corresponding function, namely method caching.
Jetcache method cache
jetcache provides a method caching solution, but the name has changed. Just use the annotation @Cached above the corresponding operation interface
Step 1: Import springboot to integrate the coordinate starter corresponding to jetcache
<dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>2.6.2</version> </dependency>
Step 2: Configure the cache
jetcache: local: default: type: linkedhashmap keyConvertor: fastjson remote: default: type: redis host: localhost port: 6379 keyConvertor: fastjson valueEncode: java valueDecode: java # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50 sms: type: redis host: localhost port: 6379 # Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion when the Object type data enters redis in redis. keyCovertor: fastjson valueEncode: java valueDecode: java poolconfig: maxTotal: 50
Since the redis cache does not support saving objects, it is necessary to set how to perform type conversion for redis when the Object type data enters into redis. The keyConvertor needs to be configured to indicate the type conversion method of the key, and at the same time, the conversion type method of the value should be marked. When the value enters redis, it is of java type, and the valueEncode is marked as java.
Note that in order to implement the value of Object type in and out of redis, it is necessary to ensure that the data of type Object in and out of redis must implement the serialization interface.
@Data public class Book implements Serializable { private Integer id; private String type; private String name; private String description; }
Step 3: Enable the method cache function when the cache is enabled, and configure the basePackages to indicate in which packages the method cache is enabled
@SpringBootApplication //jetcache main switch to enable caching @EnableCreateCacheAnnotation //Enable method annotation caching @EnableMethodCache(basePackages = "com.hashnode") public class Springboot20JetCacheApplication { public static void main(String[] args) { SpringApplication.run(Springboot20JetCacheApplication.class, args); } }
Step 4: Use the annotation @Cached to mark the current method to use the cache
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; @Override @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE) public Book getById(Integer id) { return bookDao.selectById(id); } }
Data synchronization for remote scenarios
Since the data saved by redis in the remote scheme can be shared by multiple clients, there is a data synchronization problem. jetcache provides 3 annotations to solve this problem, synchronizing the cached data during update and delete operations, and periodically refreshing the data when reading the cache
refresh cache
@CacheUpdate(name="book_",key="#book.id",value="#book") public boolean update(Book book) { return bookDao.updateById(book) > 0; }
delete cache
@CacheInvalidate(name="book_",key = "#id") public boolean delete(Integer id) { return bookDao.deleteById(id) > 0; }
Periodically refresh the cache
@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE) // 5s refresh once @CacheRefresh(refresh = 5) public Book getById(Integer id) { return bookDao.selectById(id); }
data report
jetcache also provides a simple data report function to help developers quickly view cache hit information, just add a configuration
jetcache: statIntervalMinutes: 1
After setting, output cache data hit information on the console every 1 minute
[DefaultExecutor] c.alicp.jetcache.support.StatInfoLogger : jetcache stat from 2022-02-28 09:32:15,892 to 2022-02-28 09:33:00,003 cache | qps| rate| get| hit| fail| expire| avgLoadTime| maxLoadTime ---------+-------+-------+------+-------+-------+---------+--------------+-------------- book_ | 0.66| 75.86%| 29| 22| 0| 0| 28.0| 188 ---------+-------+-------+------+-------+-------+---------+--------------+--------------
Summarize
- jetcache is a caching solution similar to springcache. It does not have a caching function. It provides a multi-level caching solution that uses local caching and remote caching.
- The caching solutions provided by jetcache are limited by the currently supported solutions. Local caching supports two and remote caching supports two.
- Pay attention to the type conversion problem when the data enters the remote cache
- jetcache provides method caching, and provides the corresponding cache update and refresh functions
- jetcache provides a simple cache information hit report to facilitate developers to monitor cache data hits in real time
think
jetcache solves the problem of using a single cache solution in the early stage, but it still cannot flexibly choose the cache for use. Is there a technology that can be used flexibly with various caches? Yes, let's talk about it in the next section.
SpringBoot integrates j2cache caching
jetcache can build a multi-level cache within a limited range, but it is not flexible enough to match the cache at will. This section introduces a cache integration framework, j2cache, that can be freely matched with a caching solution. Let's explain how to use this caching framework, taking the integration of Ehcache and redis as an example:
Step 1: Import j2cache, redis, ehcache coordinates
<dependency> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-core</artifactId> <version>2.8.4-release</version> </dependency> <dependency> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-spring-boot2-starter</artifactId> <version>2.8.0-release</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
The starter of j2cache contains redis coordinates by default. It is officially recommended to use redis as the secondary cache, so there is no need to import redis coordinates here.
Step 2: Configure the first-level and second-level caches, and configure the data transfer method between the first-level and second-level caches. The configuration is written in a file named j2cache.properties. If you use ehcache, you also need to add the ehcache configuration file separately
# Level 1 cache j2cache.L1.provider_class = ehcache ehcache.configXml = ehcache.xml # Level 2 cache j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider j2cache.L2.config_section = redis redis.hosts = localhost:6379 # How data in level 1 cache arrives at level 2 cache j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
The configuration here cannot be arbitrarily configured, and you need to refer to the official configuration instructions. For example, if the tier 1 supplier chooses ehcache, the supplier name is just an ehcache, but when the tier 2 supplier chooses redis, they need to write a dedicated Spring-integrated Redis supplier class name SpringRedisProvider, and this name is not available in all redis packages. , also not provided in the spring package. Therefore, to configure j2cache, you must refer to the official document configuration, and you must also find a dedicated integration package and import the corresponding coordinates before it can be used.
The most important configuration of the first-level and second-level caches is the data communication method between the two. This type of configuration is not arbitrarily configured, and the data communication methods provided by different caching solutions are very different. You need to query the official website. document to set.
Step ③: Use the cache
@Service public class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils; @Autowired private CacheChannel cacheChannel; public String sendCodeToSMS(String tele) { String code = codeUtils.generator(tele); cacheChannel.set("sms",tele,code); return code; } public boolean checkCode(SMSCode smsCode) { String code = cacheChannel.get("sms",smsCode.getTele()).asString(); return smsCode.getCode().equals(code); } }
The use of j2cache is similar to jetcache, but you don’t need to enable the switch to use, you can directly define the cache object and use it. The cache object name is CacheChannel.
The use of j2cache is not complicated, and the configuration is the core of j2cache. After all, it is an integrated caching framework. There are too many cache-related configurations. You can refer to the description in the j2cache.properties file in the j2cache-core core package. as follows:
#J2Cache configuration ######################################### # Cache Broadcast Method # values: # jgroups -> use jgroups's multicast # redis -> use redis publish/subscribe mechanism (using jedis) # lettuce -> use redis publish/subscribe mechanism (using lettuce, Recommend) # rabbitmq -> use RabbitMQ publisher/consumer mechanism # rocketmq -> use RocketMQ publisher/consumer mechanism # none -> don't notify the other nodes in cluster # xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy ######################################### j2cache.broadcast = redis # jgroups properties jgroups.channel.name = j2cache jgroups.configXml = /network.xml # RabbitMQ properties rabbitmq.exchange = j2cache rabbitmq.host = localhost rabbitmq.port = 5672 rabbitmq.username = guest rabbitmq.password = guest # RocketMQ properties rocketmq.name = j2cache rocketmq.topic = j2cache # use ; to split multi hosts rocketmq.hosts = 127.0.0.1:9876 ######################################### # Level 1&2 provider # values: # none -> disable this level cache # ehcache -> use ehcache2 as level 1 cache # ehcache3 -> use ehcache3 as level 1 cache # caffeine -> use caffeine as level 1 cache(only in memory) # redis -> use redis as level 2 cache (using jedis) # lettuce -> use redis as level 2 cache (using lettuce) # readonly-redis -> use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available. # memcached -> use memcached as level 2 cache (xmemcached), # [classname] -> use custom provider ######################################### j2cache.L1.provider_class = caffeine j2cache.L2.provider_class = redis # When L2 provider isn't `redis`, using `L2.config_section = redis` to read redis configurations # j2cache.L2.config_section = redis # Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true) # NOTICE: redis hash mode (redis.storage = hash) do not support this feature) j2cache.sync_ttl_to_redis = true # Whether to cache null objects by default (default false) j2cache.default_cache_null_object = true ######################################### # Cache Serialization Provider # values: # fst -> using fast-serialization (recommend) # kryo -> using kryo serialization # json -> using fst's json serialization (testing) # fastjson -> using fastjson serialization (embed non-static class not support) # java -> java standard # fse -> using fse serialization # [classname implements Serializer] ######################################### j2cache.serialization = json #json.map.person = net.oschina.j2cache.demo.Person ######################################### # Ehcache configuration ######################################### # ehcache.configXml = /ehcache.xml # ehcache3.configXml = /ehcache3.xml # ehcache3.defaultHeapSize = 1000 ######################################### # Caffeine configuration # caffeine.region.[name] = size, xxxx[s|m|h|d] # ######################################### caffeine.properties = /caffeine.properties ######################################### # Redis connection configuration ######################################### ######################################### # Redis Cluster Mode # # single -> single redis server # sentinel -> master-slaves servers # Cluster - > cluster servers (invalid database configuration, use database = 0) # Sharded - > sharded servers (password, database must be specified in hosts, and connection pool configuration is invalid; redis://user:password@127.0.0.1:6379/0) # ######################################### redis.mode = single #redis storage mode (generic|hash) redis.storage = generic ## redis pub/sub channel name redis.channel = j2cache ## redis pub/sub server (using redis.hosts when empty) redis.channel.host = #cluster name just for sharded redis.cluster_name = j2cache ## redis cache namespace optional, default[empty] redis.namespace = ## redis command scan parameter count, default[1000] #redis.scanCount = 1000 ## connection # Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379 redis.hosts = 127.0.0.1:6379 redis.timeout = 2000 redis.password = redis.database = 0 redis.ssl = false ## redis pool properties redis.maxTotal = 100 redis.maxIdle = 10 redis.maxWaitMillis = 5000 redis.minEvictableIdleTimeMillis = 60000 redis.minIdle = 1 redis.numTestsPerEvictionRun = 10 redis.lifo = false redis.softMinEvictableIdleTimeMillis = 10 redis.testOnBorrow = true redis.testOnReturn = false redis.testWhileIdle = true redis.timeBetweenEvictionRunsMillis = 300000 redis.blockWhenExhausted = false redis.jmxEnabled = false ######################################### # Lettuce scheme # # redis -> single redis server # rediss -> single redis server with ssl # redis-sentinel -> redis sentinel # redis-cluster -> cluster servers # ######################################### ######################################### # Lettuce Mode # # single -> single redis server # sentinel -> master-slaves servers # Cluster - > cluster servers (invalid database configuration, use database = 0) # Sharded - > sharded servers (password, database must be specified in hosts, and connection pool configuration is invalid; redis://user:password@127.0.0.1:6379/0) # ######################################### ## redis command scan parameter count, default[1000] #lettuce.scanCount = 1000 lettuce.mode = single lettuce.namespace = lettuce.storage = hash lettuce.channel = j2cache lettuce.scheme = redis lettuce.hosts = 127.0.0.1:6379 lettuce.password = lettuce.database = 0 lettuce.sentinelMasterId = lettuce.maxTotal = 100 lettuce.maxIdle = 10 lettuce.minIdle = 10 # timeout in milliseconds lettuce.timeout = 10000 # redis cluster topology refresh interval in milliseconds lettuce.clusterTopologyRefresh = 3000 ######################################### # memcached server configurations # refer to https://gitee.com/mirrors/XMemcached ######################################### memcached.servers = 127.0.0.1:11211 memcached.username = memcached.password = memcached.connectionPoolSize = 10 memcached.connectTimeout = 1000 memcached.failureMode = false memcached.healSessionInterval = 1000 memcached.maxQueuedNoReplyOperations = 100 memcached.opTimeout = 100 memcached.sanitizeKeys = false
Summarize
- j2cache is a caching framework that does not have a caching function by itself. It provides solutions for integrating multiple caches.
- j2cache needs to set up all levels of cache through complex configuration, as well as the way of data exchange between caches
- The j2cache operation interface is implemented through CacheChannel