Commit aad05817773d465c5fc335bc914c04ac0ad011c7
1 parent
0af6d5a7
【代码评审】IoT:插件机制
Showing
7 changed files
with
27 additions
and
31 deletions
.vscode/settings.json deleted
| 1 | -{ | |
| 2 | - "java.compile.nullAnalysis.mode": "automatic", | |
| 3 | - "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable", | |
| 4 | - "java.configuration.updateBuildConfiguration": "interactive" | |
| 5 | -} | |
| 6 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java
| ... | ... | @@ -13,8 +13,8 @@ import java.util.Arrays; |
| 13 | 13 | @Getter |
| 14 | 14 | public enum IotPluginDeployTypeEnum implements IntArrayValuable { |
| 15 | 15 | |
| 16 | - DEPLOY_VIA_JAR(0, "通过 jar 部署"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈 | |
| 17 | - DEPLOY_STANDALONE(1, "独立部署"); | |
| 16 | + JAR(0, "JAR 部署"), | |
| 17 | + STANDALONE(1, "独立部署"); | |
| 18 | 18 | |
| 19 | 19 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray(); |
| 20 | 20 | |
| ... | ... | @@ -48,4 +48,5 @@ public enum IotPluginDeployTypeEnum implements IntArrayValuable { |
| 48 | 48 | public int[] array() { |
| 49 | 49 | return ARRAYS; |
| 50 | 50 | } |
| 51 | + | |
| 51 | 52 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java
| ... | ... | @@ -33,19 +33,22 @@ public class PluginInstanceDO extends BaseDO { |
| 33 | 33 | */ |
| 34 | 34 | private String mainId; |
| 35 | 35 | /** |
| 36 | - * 插件id | |
| 36 | + * 插件 ID | |
| 37 | 37 | * <p> |
| 38 | 38 | * 关联 {@link PluginInfoDO#getId()} |
| 39 | 39 | */ |
| 40 | 40 | private Long pluginId; |
| 41 | + | |
| 41 | 42 | /** |
| 42 | - * 插件主程序所在ip | |
| 43 | + * 插件主程序所在 IP | |
| 43 | 44 | */ |
| 44 | 45 | private String ip; |
| 45 | 46 | /** |
| 46 | 47 | * 插件主程序端口 |
| 47 | 48 | */ |
| 48 | 49 | private Integer port; |
| 50 | + | |
| 51 | + // TODO @haohao:字段改成 heartbeatTime,LocalDateTime | |
| 49 | 52 | /** |
| 50 | 53 | * 心跳时间,心路时间超过 30 秒需要剔除 |
| 51 | 54 | */ | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java
| ... | ... | @@ -100,7 +100,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { |
| 100 | 100 | // 2. 停止并卸载旧的插件 |
| 101 | 101 | pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); |
| 102 | 102 | |
| 103 | - // 3 上传新的插件文件,更新插件启用状态文件 | |
| 103 | + // 3 上传新的插件文件,更新插件启用状态文件 | |
| 104 | 104 | String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); |
| 105 | 105 | |
| 106 | 106 | // 4. 更新插件信息 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java
| ... | ... | @@ -41,7 +41,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU |
| 41 | 41 | public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 42 | 42 | |
| 43 | 43 | // TODO @haohao:这个可以后续确认下,有没更合适的标识。例如说 mac 地址之类的 |
| 44 | - // 简化的UUID + mac 地址 会不会好一些,一台机子有可能会部署多个插件 | |
| 44 | + // 简化的 UUID + mac 地址 会不会好一些,一台机子有可能会部署多个插件; | |
| 45 | + // 那就 mac@uuid ? | |
| 45 | 46 | public static final String MAIN_ID = IdUtil.fastSimpleUUID(); |
| 46 | 47 | |
| 47 | 48 | @Resource |
| ... | ... | @@ -53,13 +54,13 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 53 | 54 | |
| 54 | 55 | @Value("${pf4j.pluginsDir}") |
| 55 | 56 | private String pluginsDir; |
| 56 | - | |
| 57 | 57 | @Value("${server.port:48080}") |
| 58 | 58 | private int port; |
| 59 | 59 | |
| 60 | 60 | @Override |
| 61 | 61 | public void stopAndUnloadPlugin(String pluginKey) { |
| 62 | 62 | PluginWrapper plugin = pluginManager.getPlugin(pluginKey); |
| 63 | + // TODO @haohao:改成 if return 会更简洁一点; | |
| 63 | 64 | if (plugin != null) { |
| 64 | 65 | if (plugin.getPluginState().equals(PluginState.STARTED)) { |
| 65 | 66 | pluginManager.stopPlugin(pluginKey); // 停止插件 |
| ... | ... | @@ -75,6 +76,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 75 | 76 | @Override |
| 76 | 77 | public void deletePluginFile(PluginInfoDO pluginInfoDO) { |
| 77 | 78 | File file = new File(pluginsDir, pluginInfoDO.getFileName()); |
| 79 | + // TODO @haohao:改成 if return 会更简洁一点; | |
| 78 | 80 | if (file.exists()) { |
| 79 | 81 | try { |
| 80 | 82 | TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 |
| ... | ... | @@ -82,9 +84,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 82 | 84 | log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); |
| 83 | 85 | } |
| 84 | 86 | } catch (InterruptedException e) { |
| 85 | - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), | |
| 86 | - e); | |
| 87 | - Thread.currentThread().interrupt(); // 恢复中断状态 | |
| 87 | + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); | |
| 88 | 88 | } |
| 89 | 89 | } |
| 90 | 90 | } |
| ... | ... | @@ -120,6 +120,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 120 | 120 | String pluginKey = pluginInfoDo.getPluginKey(); |
| 121 | 121 | PluginWrapper plugin = pluginManager.getPlugin(pluginKey); |
| 122 | 122 | |
| 123 | + // TODO @haohao:改成 if return 会更简洁一点; | |
| 123 | 124 | if (plugin != null) { |
| 124 | 125 | // 启动插件 |
| 125 | 126 | if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) |
| ... | ... | @@ -143,46 +144,41 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 143 | 144 | |
| 144 | 145 | @Override |
| 145 | 146 | public void reportPluginInstances() { |
| 146 | - // 1. 获取 pf4j 插件列表 | |
| 147 | + // 1.1 获取 pf4j 插件列表 | |
| 147 | 148 | List<PluginWrapper> plugins = pluginManager.getPlugins(); |
| 148 | 149 | |
| 149 | - // 2. 获取插件信息列表并转换为 Map 以便快速查找 | |
| 150 | + // 1.2 获取插件信息列表并转换为 Map 以便快速查找 | |
| 150 | 151 | List<PluginInfoDO> pluginInfos = pluginInfoMapper.selectList(); |
| 151 | 152 | Map<String, PluginInfoDO> pluginInfoMap = pluginInfos.stream() |
| 152 | 153 | .collect(Collectors.toMap(PluginInfoDO::getPluginKey, Function.identity())); |
| 153 | 154 | |
| 154 | - // 3. 获取本机 IP 和 MAC 地址 | |
| 155 | + // 1.3 获取本机 IP 和 MAC 地址 | |
| 155 | 156 | String ip = NetUtil.getLocalhostStr(); |
| 156 | 157 | String mac = NetUtil.getLocalMacAddress(); |
| 157 | 158 | String mainId = MAIN_ID + "-" + mac; |
| 158 | 159 | |
| 159 | - // 4. 遍历插件列表,并保存为插件实例 | |
| 160 | + // 2. 遍历插件列表,并保存为插件实例 | |
| 160 | 161 | for (PluginWrapper plugin : plugins) { |
| 161 | 162 | String pluginKey = plugin.getPluginId(); |
| 162 | 163 | |
| 163 | - // 4.1 查找插件信息 | |
| 164 | + // 2.1 查找插件信息 | |
| 164 | 165 | PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey); |
| 165 | 166 | if (pluginInfo == null) { |
| 166 | - // 4.2 插件信息不存在,记录错误并跳过 | |
| 167 | 167 | log.error("插件信息不存在,pluginKey = {}", pluginKey); |
| 168 | 168 | continue; |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | - // 4.3 查询插件实例 | |
| 171 | + // 2.2 情况一:如果插件实例不存在,则创建 | |
| 172 | 172 | PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(mainId, |
| 173 | 173 | pluginInfo.getId()); |
| 174 | 174 | if (pluginInstance == null) { |
| 175 | 175 | // 4.4 如果插件实例不存在,则创建 |
| 176 | - pluginInstance = PluginInstanceDO.builder() | |
| 177 | - .pluginId(pluginInfo.getId()) | |
| 178 | - .mainId(MAIN_ID + "-" + mac) | |
| 179 | - .ip(ip) | |
| 180 | - .port(port) | |
| 181 | - .heartbeatAt(System.currentTimeMillis()) | |
| 182 | - .build(); | |
| 176 | + pluginInstance = PluginInstanceDO.builder().pluginId(pluginInfo.getId()).mainId(MAIN_ID + "-" + mac) | |
| 177 | + .ip(ip).port(port).heartbeatAt(System.currentTimeMillis()).build(); | |
| 183 | 178 | pluginInstanceMapper.insert(pluginInstance); |
| 184 | 179 | } else { |
| 185 | - // 4.5 如果插件实例存在,则更新心跳时间 | |
| 180 | + // 2.2 情况二:如果存在,则更新 heartbeatAt | |
| 181 | + // TODO @haohao:这里最好 new 去 update;避免并发更新(虽然目前没有) | |
| 186 | 182 | pluginInstance.setHeartbeatAt(System.currentTimeMillis()); |
| 187 | 183 | pluginInstanceMapper.updateById(pluginInstance); |
| 188 | 184 | } | ... | ... |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java