TB支持两种缓存:Caffeine和Redis,通过配置cache.type来指定使用哪种缓存。

位于 org.thingsboard.server.cache
配置类:CaffeineCacheConfiguration
@Configuration
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@ConfigurationProperties(prefix = "caffeine")
@EnableCaching
@Data
@Slf4j
public class CaffeineCacheConfiguration {// 具体缓存的配置private Map specs;// 构造CacheManager对象,被其管理的缓存可以在Cacheable等注解中使用@Beanpublic CacheManager cacheManager() {log.trace("Initializing cache: {} specs {}", Arrays.toString(RemovalCause.values()), specs);SimpleCacheManager manager = new SimpleCacheManager();if (specs != null) {// 加载配置文件中定义的具体缓存配置List caches = specs.entrySet().stream().map(entry -> buildCache(entry.getKey(), entry.getValue())).collect(Collectors.toList());manager.setCaches(caches);}//SimpleCacheManager is not a bean (will be wrapped), so call initializeCaches manuallymanager.initializeCaches();// 支持事务return new TransactionAwareCacheManagerProxy(manager);}// 构造一个缓存对象private CaffeineCache buildCache(String name, CacheSpecs cacheSpec) {final Caffeine
配置类上的 @ConditionalOnProperty(prefix = “cache”, value = “type”, havingValue = “caffeine”, matchIfMissing = true)说明当配置使用caffeine缓存时,该配置类才生效.
public static void main(String[] args) throws InterruptedException {Cache cache = Caffeine.newBuilder()//设置最大权重为20.maximumWeight(20)//设置值的权重等于值.weigher((key, value) -> (int) value).removalListener((Integer key, Object value, RemovalCause cause) ->System.out.printf("Key %s was removed because (%s)%n", key, cause)).build();// 插入权重为19的键值对cache.put(0, 19);//打印缓存个数,结果为1System.out.println(cache.estimatedSize());// 插入权重为2的键值对,累计权重为21,会触发空间回收,基于TinyLRU算法驱逐key=0的键值对cache.put(1, 2);// 继续插入,累计权重为4,不会触发空间回收cache.put(2, 2);//稍微休眠一秒Thread.sleep(1000);//打印缓存个数,结果为2System.out.println(cache.estimatedSize());
}// 控制台打印:
//1
//Key 0 was removed because (SIZE) key 0 被驱逐因为缓存大小限制
//2
假设方法updateUser被@CachePut修饰,则更新User后更新缓存
在一个事务方法中先调用updateUser方法,然后调用addUpdateLog方法;
在实际执行时,执行addUpdateLog方法发生异常,导致事务回滚;
如果缓存不支持事务,则会导致缓存脏数据的问题,流程如下:

配置类:TBRedisCacheConfiguration
// 连接池
@Bean
public RedisConnectionFactory redisConnectionFactory() {return loadFactory();
}// 支持Redis多种部署模式,因此由子类定义
protected abstract JedisConnectionFactory loadFactory();/*** 传入连接工厂Bean定义RedisCacheManager,支持事务* Transaction aware RedisCacheManager.* Enable RedisCaches to synchronize cache put/evict operations with ongoing Spring-managed transactions.*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory cf) {DefaultFormattingConversionService redisConversionService = new DefaultFormattingConversionService();RedisCacheConfiguration.registerDefaultConverters(redisConversionService);registerDefaultConverters(redisConversionService);RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig().withConversionService(redisConversionService);return RedisCacheManager.builder(cf).cacheDefaults(configuration).transactionAware().build();
}@Bean
public RedisTemplate redisTemplate() {RedisTemplate template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory());return template;
}
支持Redis的两种模式:Standalone和Cluster;因此构造连接方法在两个子类中定义:

因为使用了CacheManager因此可以直接使用注解来使用缓存
cacheNmaes指的是使用哪个缓存,当使用Caffeine缓存时会根据配置文件加载多个缓存Bean: