背景
如果各位看官是分布式項(xiàng)目應(yīng)該都采用分布式緩存了,例如redis等,分布式緩存不在本次討論范圍哈;我個(gè)人建議是,如果是用戶量比較大,建議采用分布式緩存機(jī)制,后期可以很容易前后到分布式服務(wù)或微服務(wù)。
我這邊項(xiàng)目基本上都是單體架構(gòu),因?yàn)闃I(yè)務(wù)場(chǎng)景需要,用戶一般就幾十個(gè),最多,最多也就是100多用戶,所以,單體是完全滿足的,同時(shí)用戶對(duì)于系統(tǒng)的要求也不高,因此采用了單體架構(gòu),但是后期可以切換到分布式,這是后期需求,如果遇到在調(diào)整。
問題
ok,背景介紹完。那就是說下單體的問題出現(xiàn)的情況;由于業(yè)務(wù)場(chǎng)景需要,很多的應(yīng)用緩存和功能局部緩存,一開始設(shè)計(jì)這塊時(shí),就沒好好設(shè)計(jì)這塊,現(xiàn)在遇到問題了;第一個(gè),應(yīng)用緩存比較亂(也有公共的緩存機(jī)制,但是有些業(yè)務(wù)不希望放到公共緩存里,一般就在當(dāng)前類上定義了緩存),第二個(gè)線上也出現(xiàn)了幾次因?yàn)榫彺嫖醇皶r(shí)刷新,造成垃圾數(shù)據(jù)的產(chǎn)生,因此,在這些問題的出現(xiàn)后,需要進(jìn)一步設(shè)計(jì)緩存機(jī)制了,在不大調(diào)整業(yè)務(wù)代碼的前提下如何及時(shí)進(jìn)行刷新應(yīng)用緩存呢?
應(yīng)用緩存刷新
一開始刷新機(jī)制很簡(jiǎn)單,有模糊匹配緩存key進(jìn)行刪除,有指定key進(jìn)行刪除的,但是總有寫業(yè)務(wù)寫的不太規(guī)范,有些亂,關(guān)鍵后期也不曉得,在什么情況下進(jìn)行刷新了緩存,這樣的操作將會(huì)給后期項(xiàng)目遺留問題;怎么解決呢?方案是創(chuàng)建緩存接口,需要進(jìn)行實(shí)現(xiàn)接口,然后需要進(jìn)行緩存刷新的,實(shí)現(xiàn)該接口進(jìn)行處理,什么時(shí)間進(jìn)行刷新呢,根據(jù)業(yè)務(wù),進(jìn)行通知即可;現(xiàn)在面臨著一個(gè)問題,有些類是交給spring管理的,有些類是項(xiàng)目自己管理的并沒有交給spring,這個(gè)問題如何解決呢?也好解決,第一種,全部交給spring容器管理,通過spring拿到所有刷新緩存接口實(shí)現(xiàn)類,進(jìn)行循環(huán)調(diào)用刷新接口,這塊屬于一刀切,不過比較簡(jiǎn)單,我這邊沒有采用;第二種是原有代碼不調(diào)整,之前什么樣就是什么樣,那就要有spi機(jī)制,這塊可以采用谷歌服務(wù)發(fā)現(xiàn)方式,這里還需要考慮一個(gè)問題,如果交給spring就不用spi發(fā)現(xiàn),沒有交給spring就需要進(jìn)行發(fā)現(xiàn),但是為了防止重復(fù),優(yōu)先使用sping機(jī)制進(jìn)行獲取所偶刷新解決的實(shí)現(xiàn)類,然后通過spi找到所有實(shí)現(xiàn)的類,進(jìn)行排查把增量的實(shí)現(xiàn)類添加給緩存類進(jìn)行統(tǒng)一循環(huán)處理即可文章來源:http://www.zghlxwxcb.cn/news/detail-615143.html
demo:文章來源地址http://www.zghlxwxcb.cn/news/detail-615143.html
@Service
@Slf4j
public class RefreshCacheUtils implements ApplicationContextAware, ApplicationRunner {
private final static List<RefreshCache> 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<String, RefreshCache> beans = applicationContext.getBeansOfType(RefreshCache.class);
//使用jdk提供的類ServiceLoader來加載RefreshCache的子類
//如果服務(wù)發(fā)現(xiàn)沒有,請(qǐng)檢查RefreshCache實(shí)現(xiàn)類是否添加注解:@AutoService(value = RefreshCache.class)
//如果實(shí)現(xiàn)了RefreshCache的對(duì)應(yīng)的類是被spring管理,就不用加@AutoService(value = RefreshCache.class),這塊邏輯已經(jīng)進(jìn)行整合了
ServiceLoader<RefreshCache> loaders = ServiceLoader.load(RefreshCache.class);
//把spring容器里的Cache實(shí)現(xiàn)類直接放入緩存
REFRESH_CACHE_LIST.addAll(beans.values());
//在進(jìn)行處理spi
loaders.stream().forEach(s -> {
//是否已經(jīng)存在spring容器進(jìn)行打標(biāo)記
AtomicBoolean isExistFlag = new AtomicBoolean(false);
for (RefreshCache refreshCache : REFRESH_CACHE_LIST) {
String name = refreshCache.getClass().getName();
String spiName = s.get().getClass().getName();
//判斷spi的對(duì)象是否在spring容器里,如果在直接打上標(biāo)記為true并結(jié)束循環(huán)
if (name.equals(spiName) || name.contains(spiName + "$$")) {
isExistFlag.set(true);
break;
}
}
//判斷標(biāo)記是否為true,如果為true就說明都不處理,如果為false,加入到刷新緩存列表
if (!isExistFlag.get()) {
REFRESH_CACHE_LIST.add(s.get());
}
});
log.info("所有待刷新緩存對(duì)象信息初始化完成:{}", REFRESH_CACHE_LIST);
}
/**
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
到了這里,關(guān)于關(guān)于單體架構(gòu)緩存刷新實(shí)現(xiàn)方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!