动态加载smart-reload

9/7/2021

# SmartReload说明

可能一看到这个名字会很困惑,不知道什么意思。但是接下来我们想象一个场景:

比如一个电商: 如果你把一些商品信息放到java进程里的缓存里,那么当你需要更新某两个商品信息的时候怎么办?

有人说为啥不使用redis缓存? 额...并不是所有缓存都有必要使用redis 有人说不使用缓存?额...那还不如不用程序算了 有人说 重启?额...做好没年终奖的准备吧

所以系统需要有这个东西,在不重启java程序的前提下,能执行一个预留的代码。

这种场景叫做Reload

# 解决方案

其实有很多种解决方案,最经典的应该是 轮询和订阅这两种。 轮询:每个一段时间重新查询数据库 订阅:系统订阅消息,然后使用第三者发送消息给系统,进行一些操作

# SmartAdmin的选择:轮询

SmartAdmin中的reload选择轮询+监听者模式策略。 原因:

  1. 轮询比订阅简单,订阅需要依赖其他第三方:比如redis订阅,kafka等MQ订阅,Zookeeper等
  2. 轮询可以专注于本应用

# 设计思想

启动线程去扫描某个表,表中存放着一些 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`)
  )
1
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);
}
1
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));
            }
        }
    }
}
1
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;
    }

1
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创新实验室 喝胡辣汤~
告白气球 (钢琴版)
JESSE T