Commit aad05817773d465c5fc335bc914c04ac0ad011c7

Authored by YunaiV
1 parent 0af6d5a7

【代码评审】IoT:插件机制

.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
... ... @@ -11,7 +11,7 @@ import org.springframework.web.multipart.MultipartFile;
11 11 public interface PluginInstanceService {
12 12  
13 13 /**
14   - * 上报插件实例
  14 + * 上报插件实例(心跳)
15 15 */
16 16 void reportPluginInstances();
17 17  
... ...
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
... ... @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController;
11 11 import javax.annotation.Resource;
12 12 import java.util.concurrent.CompletableFuture;
13 13  
  14 +// TODO 芋艿:后续 review 下
14 15 /**
15 16 * 插件实例 RPC 接口
16 17 *
... ...