H5W3
当前位置:H5W3 > 其他技术问题 > 正文

项目冗余代码怎么处理?

公司需要改造现有的敏感词过滤机制, 从文件读取+本地缓存的方式变成, 读数据库+redis+本地缓存的方式;
这个任务交给了我, 之前的方式可以将敏感词库初始化, 过滤方法等代放在通用工具包中, 但是现在需要使用redis或者读取数据库等, 需要在spring的容器中被管理, 而敏感词过滤又被多个项目使用, 如过每个项目都写一遍的话, 改动的时候就很麻烦, 求指点更好的解决方案, 谢谢

回答:

从之前的大家的回答和讨论来看,微服务是被题主否定了,这肯定是多方面的考虑嘛,并且微服务只是一种方案的选择而已,也并不是一招吃遍天下的,很显然之前代码的处理肯定是按照引用一个通用工具jar包调用的方式处理,所以改为微服务的话,就算不谈此次缓存和数据库的修改逻辑来说,光架构方面的改动,调用方和提供方改动都还是比较大的。还有双方开发人员对于微服务的熟悉程度。

所以我起初看到这个问题的时候,我反而还没有想用微服务的,因为从题主的描述来看,我感觉他更希望用一种结合Spring整合缓存,然后替换之前的工具包里的逻辑而已,这样相当于只是改了之前工具包里的逻辑,就算调用接口被改,调用方修改的代价也是比较小的,改改方法名和入参而已。

因此我谈谈我自己的看法吧,我能想到的Spring整合缓存那肯定是Spring CacheSpring作为一个整合框架,要的就是一招可以针对某个业务提出统一接口,然后第三方实现,相当于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做处理缓存起来

如上的描述其实基本就满足了题主的需求,只是有一点,题主是有两级缓存的,刚刚的描述好像没有提到,其实不然,我们来看SpringCacheManager的接口定义

image.png

根据缓存的name返回一个Cache,其实Cache就是某种类型的缓存的抽象,比如有Redis的实现RedisCacheEhCache的实现EhCacheCache等等。

但是CacheManager的定义是根据name返回一个Cache,也没有指定要什么类型的(虽然RedisCacheEhCacheCache都有他们自己的CacheManager),所以CacheManager管理的Cache理论是可以有多不同类型的实现。当然不用题主定义,Spring想好了,请看CompositeCacheManager
image.png

CompositeCacheManager如其名,组合的CacheManager,虽然有不同类型的Cache实现,也有不同的CacheManager类型实现,现在CompositeCacheManager就可以把不同类型的CacheManager合在一起,因为合在一起的方式是集合List<CacheManager>,而集合是有顺序的,所以题主这下应该明白为啥可以实现两级缓存了叭,这就是我的想法

那废话不多说,只要题主了解一下Spring Cache的相关知识,接下来我的做法应该可以理解了
本地缓存我就以EhCache举例吧,我主要展示Cache的配法,其他的配置就不展示了哈

那我们先配置一个EhCacheCacheManager: EhCacheCacheManager

@Autowired
private CacheProperties cacheProperties;

@Bean
public EhCacheCacheManager ehCacheCacheManager() {
    Resource location = this.cacheProperties.resolveConfigLocation(this.cacheProperties.getEhcache().getConfig());
 return new EhCacheCacheManager(EhCacheManagerUtils.buildCacheManager(location));
}

接下来再配一个RedisCacheManagerRedisCacheManager

@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
    RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
            .build();
 return cacheManager;
}

两个CacheManager都有了,关键的,我们再配置一个新的CacheManagerCompositeCacheManager

@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里的名字要和这里一致

image.png

至于RedisREDIS_DIRTY_DATA,这个可以随意了,毕竟没有的话,默认情况下,它是可以自己造一个的,参考RedisCacheManagergetMissingCache

image.png

我把完整代码放到github上吧,我提供的方案只是我自己对于你描述的业务的理解,虽然不一定能解决你的问题,不过Spring Cache是你可以考虑的方向哈

那就这样叭~拜了个拜(ˉ▽ ̄~)

回答:

这种就应该用微服务的概念,使用微服务

回答:

敏感词过滤 这是个相对独立的功能,独立成一个单独的服务,提供 api 接口给其他服务使用吧.

回答:

旧系统十有八九是静态方法,调用直接了事,
改造的话,直接改调用的方法就行了;
需要注意的地方就是通常类也要使用bean:

因为涉及到db查询和redis,需要使用到相关的bean,在最小的改动下,可以通过实现ApplicationContextAware接口来获取applciationContext,有这玩意,就能在没有被容器管理的类也可以使用bean了

不过这种缺点的话 也很明显
如果是多个业务系统都要做这个功能,数据存在业务系统数据库的话,这部分表都要有
(我们这边就有这种业务,之前数据都是在一个单独的服务,后面为了精简那个服务的功能,将这一部分数据抽到业务系统了,现在业务系统都有一部分完全一样的表,感觉有点没必要)

回答:

封装JAR提供给子应用使用, 这个jar可以直接操作DB、redis . 且不影响接入的项目

本文地址:H5W3 » 项目冗余代码怎么处理?

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址