公司需要改造现有的敏感词过滤机制, 从文件读取+本地缓存的方式变成, 读数据库+redis+本地缓存的方式;
这个任务交给了我, 之前的方式可以将敏感词库初始化, 过滤方法等代放在通用工具包中, 但是现在需要使用redis或者读取数据库等, 需要在spring的容器中被管理, 而敏感词过滤又被多个项目使用, 如过每个项目都写一遍的话, 改动的时候就很麻烦, 求指点更好的解决方案, 谢谢
从之前的大家的回答和讨论来看,微服务是被题主否定了,这肯定是多方面的考虑嘛,并且微服务只是一种方案的选择而已,也并不是一招吃遍天下的,很显然之前代码的处理肯定是按照引用一个通用工具jar
包调用的方式处理,所以改为微服务的话,就算不谈此次缓存和数据库的修改逻辑来说,光架构方面的改动,调用方和提供方改动都还是比较大的。还有双方开发人员对于微服务的熟悉程度。
所以我起初看到这个问题的时候,我反而还没有想用微服务的,因为从题主的描述来看,我感觉他更希望用一种结合Spring
整合缓存,然后替换之前的工具包里的逻辑而已,这样相当于只是改了之前工具包里的逻辑,就算调用接口被改,调用方修改的代价也是比较小的,改改方法名和入参而已。
因此我谈谈我自己的看法吧,我能想到的Spring
整合缓存那肯定是Spring Cache
,Spring
作为一个整合框架,要的就是一招可以针对某个业务提出统一接口,然后第三方实现,相当于Spring
项目级别的SPI
,那对于Cache
来说,Spring
也有抽象有一套接口和注解,可以搜索直接Spring Cache
,简单了解一下用法即可
回到题主的需求上,需要读数据库+redis
+本地缓存,那其实我自己理解这应该就是本地缓存作为一级缓存,redis
作为二级缓存,最后再去读DB
那这跟Spring Cache
的方法注解@Cacheable
就有类似的效果了
@Cacheable
标注一个方法,提供缓存的name
,对应的key
,以及使用哪个CacheManager
处理,例如下面的例子一样
@Cacheable(cacheNames = "DIRTY_DATA", key = "'all_dirty_data'", cacheManager = "CACHE_MANAGER")
public List<String> all() {
return Arrays.asList("data1", "data2");
}
如果这次操作的CacheManager
返回为null
,那就执行方法all()
,并且方法all()
的值最后会被交给CacheManager
中的Cache
做处理缓存起来
如上的描述其实基本就满足了题主的需求,只是有一点,题主是有两级缓存的,刚刚的描述好像没有提到,其实不然,我们来看Spring
的CacheManager
的接口定义
根据缓存的name
返回一个Cache
,其实Cache
就是某种类型的缓存的抽象,比如有Redis
的实现RedisCache
,EhCache
的实现EhCacheCache
等等。
但是CacheManager
的定义是根据name
返回一个Cache
,也没有指定要什么类型的(虽然RedisCache
和EhCacheCache
都有他们自己的CacheManager
),所以CacheManager
管理的Cache
理论是可以有多不同类型的实现。当然不用题主定义,Spring
想好了,请看CompositeCacheManager
CompositeCacheManager
如其名,组合的CacheManager
,虽然有不同类型的Cache
实现,也有不同的CacheManager
类型实现,现在CompositeCacheManager
就可以把不同类型的CacheManager
合在一起,因为合在一起的方式是集合List<CacheManager>
,而集合是有顺序的,所以题主这下应该明白为啥可以实现两级缓存了叭,这就是我的想法
那废话不多说,只要题主了解一下Spring Cache
的相关知识,接下来我的做法应该可以理解了
本地缓存我就以EhCache
举例吧,我主要展示Cache
的配法,其他的配置就不展示了哈
那我们先配置一个EhCache
的CacheManager
: EhCacheCacheManager
@Autowired
private CacheProperties cacheProperties;
@Bean
public EhCacheCacheManager ehCacheCacheManager() {
Resource location = this.cacheProperties.resolveConfigLocation(this.cacheProperties.getEhcache().getConfig());
return new EhCacheCacheManager(EhCacheManagerUtils.buildCacheManager(location));
}
接下来再配一个Redis
的CacheManager
:RedisCacheManager
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
.build();
return cacheManager;
}
两个CacheManager
都有了,关键的,我们再配置一个新的CacheManager
:CompositeCacheManager
@Bean
@Primary
public CompositeCacheManager compositeCacheManager(EhCacheCacheManager ehCacheCacheManager, RedisCacheManager redisCacheManager) {
return new CompositeCacheManager(ehCacheCacheManager, redisCacheManager);
}
注意上面的顺序哈,如果想要优先EhCache
命中,当然EhCacheCacheManager
就要放在构造方法的参数中的最前面哈,并且为了@Cacheable
配置起来很简洁,我们加一个@Primary
,因为现在有三个CacheManager
了,这样的话,我们使用@Cacheable
就可以不用指定那个cacheManager
参数了
其次,为了CompositeCacheManager
和@Cacheable
能关联起来,我们需要配置一个CacheResolver
@Bean
public CacheResolver cacheResolver(CompositeCacheManager compositeCacheManager) {
return new SimpleCacheResolver(compositeCacheManager);
}
最后,就是如何使用了,假设对外提供了一个脏数据查询的接口
public interface IDirtyDataService {
List<String> all();
}
那它的实现可以这样写
@Service
public class DirtyDataServiceImpl implements IDirtyDataService {
@Override
@Cacheable(cacheNames = {"EHCACHE_DIRTY_DATA", "REDIS_DIRTY_DATA"}, key = "'all_dirty_data'")
public List<String> all() {
return Arrays.asList("data1", "data2");
}
}
非常简单哈,这其中的cacheNames
中的EHCACHE_DIRTY_DATA
,是和你配置的EhCache
的名字一样哈,因为EhCache
的配置是xml
的,所以xml
里的名字要和这里一致
至于Redis
的REDIS_DIRTY_DATA
,这个可以随意了,毕竟没有的话,默认情况下,它是可以自己造一个的,参考RedisCacheManager
的getMissingCache
我把完整代码放到github上吧,我提供的方案只是我自己对于你描述的业务的理解,虽然不一定能解决你的问题,不过Spring Cache
是你可以考虑的方向哈
那就这样叭~拜了个拜(ˉ▽ ̄~)