# SmartReload说明
可能一看到这个名字会很困惑,不知道什么意思。但是接下来我们想象一个场景:
比如一个电商: 如果你把一些商品信息放到java进程里的缓存里,那么当你需要更新某两个商品信息的时候怎么办?
有人说为啥不使用redis缓存? 额...并不是所有缓存都有必要使用redis 有人说不使用缓存?额...那还不如不用程序算了 有人说 重启?额...做好没年终奖的准备吧
所以系统需要有这个东西,在不重启java程序的前提下,能执行一个预留的代码。
这种场景叫做Reload
# 解决方案
其实有很多种解决方案,最经典的应该是 轮询和订阅这两种。 轮询:每个一段时间重新查询数据库 订阅:系统订阅消息,然后使用第三者发送消息给系统,进行一些操作
# SmartAdmin的选择:轮询
SmartAdmin中的reload选择轮询+监听者模式
策略。
原因:
- 轮询比订阅简单,订阅需要依赖其他第三方:比如redis订阅,kafka等MQ订阅,Zookeeper等
- 轮询可以专注于本应用
# 设计思想
启动线程去扫描某个表,表中存放着一些 reload项(reload item),但凡有reload项标识发生变化,就发送事件给那些监听reload项的java监听者。
具体请看:smartadmin.common.reload
# 1 表结构:
CREATE TABLE `t_reload_item` (
`tag` VARCHAR(255) NOT NULL COMMENT '项名称' COLLATE 'utf8_general_ci',
`args` VARCHAR(255) NULL DEFAULT NULL COMMENT '参数 可选',
`identification` VARCHAR(255) NOT NULL COMMENT '运行标识' COLLATE 'utf8_general_ci',
`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`tag`)
)
2
3
4
5
6
7
8
tag: reload项 args:listener执行之后的参数 identification:标识,identification与上次比较发生变化才进行reload
2 接口代码:
public interface SmartReloadCommandInterface {
/**
* 任务:
* 读取数据库中 ReloadItem 数据
* 校验是否发生变化
* 执行重加载动作
*/
void doTask();
/**
* 该方法返回一个List<ReloadItem></>:<br>
* ReloadItem对象的tagIdentify为:该tag的 状态(状态其实就是个字符串,如果该字符串跟上次有变化则进行reload操作)<br>
* ReloadItem对象的args为: reload操作需要的参数<br><br>
*
* @return List<ReloadItem>
*/
List<ReloadItem> readReloadItem();
/**
* 处理Reload结果
*
* @param reloadResult
*/
void handleReloadResult(SmartReloadResult reloadResult);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 为Spring设计的reload项比较实现类
/**
* 检测是否 Reload 的类
*
* @author zhuoda
*/
public abstract class AbstractSmartReloadCommand4Spring implements SmartReloadCommandInterface {
/**
* 当前ReloadItem的存储器
*/
protected ConcurrentHashMap<String, String> currentTags = new ConcurrentHashMap<>();
/**
* Reload的执行类
*/
@Autowired
protected SmartReloadManager reloadManager;
// @PostConstruct
public void init() {
List<ReloadItem> readTagStatesFromDb = readReloadItem();
if (readTagStatesFromDb != null) {
for (ReloadItem reloadItem : readTagStatesFromDb) {
String tag = reloadItem.getTag();
String tagChangeIdentifier = reloadItem.getIdentification();
this.currentTags.put(tag, tagChangeIdentifier);
}
}
}
/**
* 任务:
* 读取数据库中 ReloadItem 数据
* 校验是否发生变化
* 执行重加载动作
*/
@Override
public void doTask() {
// 获取数据库数据
List<ReloadItem> readTagStatesFromDb = readReloadItem();
String tag;
String tagIdentifier;
String preTagChangeIdentifier;
for (ReloadItem reloadItem : readTagStatesFromDb) {
tag = reloadItem.getTag();
tagIdentifier = reloadItem.getIdentification();
preTagChangeIdentifier = currentTags.get(tag);
// 数据不一致
if (preTagChangeIdentifier == null || ! preTagChangeIdentifier.equals(tagIdentifier)) {
// 更新map数据
currentTags.put(tag, tagIdentifier);
// 执行重新加载此项的动作
handleReloadResult(this.reloadManager.doReload(reloadItem));
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 3 样例 Demo
@Slf4j
@Service
public class SystemConfigService {
/**
* 系统配置缓存
*/
private ConcurrentHashMap<String, SystemConfigEntity> systemConfigMap = new ConcurrentHashMap<>();
@Autowired
private SystemConfigDao systemConfigDao;
@Autowired
private SmartReloadService smartReloadService;
/**
* 初始化系统设置缓存
*/
@PostConstruct
private void initSystemConfigCache() {
//系统启动的时候,注册到smart-reload
smartReloadService.register(this);
}
@SmartReload(SmartReloadTagConst.SYSTEM_CONFIG)
public boolean reload(String args) {
this.initSystemConfigCache();
log.warn("<<SmartReload>> <<{}>> , args {} reload success ", SmartReloadTagConst.SYSTEM_CONFIG, args);
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
作者简介: 卓大 (opens new window), 1024创新实验室主任,混迹于各个技术圈,熟悉点java,略懂点前端。
![]() | ![]() | ![]() |
加“卓大”微信,入群 | 关注 1024创新实验室! | 我要请 1024创新实验室 喝胡辣汤~ |