关于单体架构缓存刷新实现方案

背景

如果各位看官是分布式项目应该都采用分布式缓存了,例如redis等,分布式缓存不在本次讨论范围哈;我个人建议是,如果是用户量比较大,建议采用分布式缓存机制,后期可以很容易前后到分布式服务或微服务。

我这边项目基本上都是单体架构,因为业务场景需要,用户一般就几十个,最多,最多也就是100多用户,所以,单体是完全满足的,同时用户对于系统的要求也不高,因此采用了单体架构,但是后期可以切换到分布式,这是后期需求,如果遇到在调整。

问题

ok,背景介绍完。那就是说下单体的问题出现的情况;由于业务场景需要,很多的应用缓存和功能局部缓存,一开始设计这块时,就没好好设计这块,现在遇到问题了;第一个,应用缓存比较乱(也有公共的缓存机制,但是有些业务不希望放到公共缓存里,一般就在当前类上定义了缓存),第二个线上也出现了几次因为缓存未及时刷新,造成垃圾数据的产生,因此,在这些问题的出现后,需要进一步设计缓存机制了,在不大调整业务代码的前提下如何及时进行刷新应用缓存呢?

应用缓存刷新

一开始刷新机制很简单,有模糊匹配缓存key进行删除,有指定key进行删除的,但是总有写业务写的不太规范,有些乱,关键后期也不晓得,在什么情况下进行刷新了缓存,这样的操作将会给后期项目遗留问题;怎么解决呢?方案是创建缓存接口,需要进行实现接口,然后需要进行缓存刷新的,实现该接口进行处理,什么时间进行刷新呢,根据业务,进行通知即可;现在面临着一个问题,有些类是交给spring管理的,有些类是项目自己管理的并没有交给spring,这个问题如何解决呢?也好解决,第一种,全部交给spring容器管理,通过spring拿到所有刷新缓存接口实现类,进行循环调用刷新接口,这块属于一刀切,不过比较简单,我这边没有采用;第二种是原有代码不调整,之前什么样就是什么样,那就要有spi机制,这块可以采用谷歌服务发现方式,这里还需要考虑一个问题,如果交给spring就不用spi发现,没有交给spring就需要进行发现,但是为了防止重复,优先使用sping机制进行获取所偶刷新解决的实现类,然后通过spi找到所有实现的类,进行排查把增量的实现类添加给缓存类进行统一循环处理即可

demo:

@Service
@Slf4j
public class RefreshCacheUtils implements ApplicationContextAware, ApplicationRunner {

    private final static List REFRESH_CACHE_LIST = new ArrayList<>();

    private ApplicationContext applicationContext;

    
    public static void refreshByDeviceId(String deviceId) {
        for (RefreshCache refreshCache : REFRESH_CACHE_LIST) {
            refreshCache.refreshByDeviceId(deviceId);
        }
    }

    
    public static void refreshByBusinessId(String businessId) {
        for (RefreshCache refreshCache : REFRESH_CACHE_LIST) {
            refreshCache.refreshByBusinessId(businessId);
        }
    }

    
    public static void refreshdDefaultExecution(String otherParameter) {
        for (RefreshCache refreshCache : REFRESH_CACHE_LIST) {
            refreshCache.defaultExecution(otherParameter);
        }
    }

    /**
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        Map beans = applicationContext.getBeansOfType(RefreshCache.class);

        //使用jdk提供的类ServiceLoader来加载RefreshCache的子类
        //如果服务发现没有,请检查RefreshCache实现类是否添加注解:@AutoService(value = RefreshCache.class)
        //如果实现了RefreshCache的对应的类是被spring管理,就不用加@AutoService(value = RefreshCache.class),这块逻辑已经进行整合了
        ServiceLoader loaders = ServiceLoader.load(RefreshCache.class);
        //把spring容器里的Cache实现类直接放入缓存
        REFRESH_CACHE_LIST.addAll(beans.values());
        //在进行处理spi
        loaders.stream().forEach(s -> {
            //是否已经存在spring容器进行打标记
            AtomicBoolean isExistFlag = new AtomicBoolean(false);

            for (RefreshCache refreshCache : REFRESH_CACHE_LIST) {
                String name = refreshCache.getClass().getName();
                String spiName = s.get().getClass().getName();
                //判断spi的对象是否在spring容器里,如果在直接打上标记为true并结束循环
                if (name.equals(spiName) || name.contains(spiName + "$$")) {
                    isExistFlag.set(true);
                    break;
                }
            }
            //判断标记是否为true,如果为true就说明都不处理,如果为false,加入到刷新缓存列表
            if (!isExistFlag.get()) {
                REFRESH_CACHE_LIST.add(s.get());
            }
        });

        log.info("所有待刷新缓存对象信息初始化完成:{}", REFRESH_CACHE_LIST);

    }

    /**
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

你可能感兴趣的:(架构,缓存)