Commit 88ef8ba2e3be7eed2add375dbc07eaf029df8742
1 parent
698cec92
【功能完善】IoT: 删除旧版 HTTP 插件,重构 HttpPlugin 以支持独立启动,新增 VertxService 管理 HTTP 服务器逻辑,优化代码…
…结构和异常处理,更新相关配置以提升插件性能和可维护性。
Showing
7 changed files
with
183 additions
and
102 deletions
plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar deleted
No preview for this file type
plugins/yudao-module-iot-plugin-http-1.0.0.jar
No preview for this file type
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.plugin.http; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.plugin.http.config.VertxService; | |
| 4 | +import lombok.extern.slf4j.Slf4j; | |
| 3 | 5 | import org.springframework.boot.SpringApplication; |
| 4 | 6 | import org.springframework.boot.WebApplicationType; |
| 5 | 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| 8 | +import org.springframework.context.ConfigurableApplicationContext; | |
| 6 | 9 | |
| 10 | +/** | |
| 11 | + * 独立运行入口 | |
| 12 | + */ | |
| 13 | +@Slf4j | |
| 7 | 14 | @SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") |
| 8 | 15 | public class HttpPluginSpringbootApplication { |
| 9 | 16 | |
| 10 | 17 | public static void main(String[] args) { |
| 18 | + // 这里可选择 NONE / SERVLET / REACTIVE,看你需求 | |
| 11 | 19 | SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); |
| 12 | 20 | application.setWebApplicationType(WebApplicationType.NONE); |
| 13 | - application.run(args); | |
| 14 | - } | |
| 15 | 21 | |
| 22 | + ConfigurableApplicationContext context = application.run(args); | |
| 23 | + | |
| 24 | + // 手动获取 VertxService 并启动 | |
| 25 | + VertxService vertxService = context.getBean(VertxService.class); | |
| 26 | + vertxService.startServer(); | |
| 27 | + | |
| 28 | + log.info("[HttpPluginSpringbootApplication] 独立模式启动完成"); | |
| 29 | + } | |
| 16 | 30 | } |
| 17 | 31 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java
| ... | ... | @@ -2,80 +2,70 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.extra.spring.SpringUtil; |
| 4 | 4 | import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; |
| 5 | -import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; | |
| 6 | -import io.vertx.core.Vertx; | |
| 7 | -import io.vertx.ext.web.Router; | |
| 8 | -import io.vertx.ext.web.handler.BodyHandler; | |
| 9 | 5 | import lombok.extern.slf4j.Slf4j; |
| 10 | 6 | import org.pf4j.PluginWrapper; |
| 11 | 7 | import org.pf4j.spring.SpringPlugin; |
| 8 | +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | |
| 12 | 9 | import org.springframework.context.ApplicationContext; |
| 13 | 10 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
| 14 | 11 | |
| 12 | +/** | |
| 13 | + * 负责插件的启动和停止,与 Vert.x 的生命周期管理 | |
| 14 | + */ | |
| 15 | 15 | @Slf4j |
| 16 | 16 | public class HttpVertxPlugin extends SpringPlugin { |
| 17 | 17 | |
| 18 | - private static final int PORT = 8092; | |
| 19 | - private Vertx vertx; | |
| 20 | - | |
| 21 | 18 | public HttpVertxPlugin(PluginWrapper wrapper) { |
| 22 | 19 | super(wrapper); |
| 23 | 20 | } |
| 24 | 21 | |
| 25 | 22 | @Override |
| 26 | 23 | public void start() { |
| 27 | - log.info("HttpVertxPlugin.start()"); | |
| 24 | + log.info("[HttpVertxPlugin] start ..."); | |
| 28 | 25 | |
| 29 | - // 获取 DeviceDataApi 实例 | |
| 30 | - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); | |
| 31 | - if (deviceDataApi == null) { | |
| 32 | - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); | |
| 26 | + // 1. 获取插件上下文 | |
| 27 | + ApplicationContext pluginContext = getApplicationContext(); | |
| 28 | + if (pluginContext == null) { | |
| 29 | + log.error("[HttpVertxPlugin] pluginContext is null, start failed."); | |
| 33 | 30 | return; |
| 34 | 31 | } |
| 35 | 32 | |
| 36 | - // 初始化 Vert.x | |
| 37 | - vertx = Vertx.vertx(); | |
| 38 | - Router router = Router.router(vertx); | |
| 39 | - | |
| 40 | - // 处理 Body | |
| 41 | - router.route().handler(BodyHandler.create()); | |
| 42 | - | |
| 43 | - // 设置路由 | |
| 44 | - router.post("/sys/:productKey/:deviceName/thing/event/property/post") | |
| 45 | - .handler(new HttpVertxHandler(deviceDataApi)); | |
| 46 | - | |
| 47 | - // 启动 HTTP 服务器 | |
| 48 | - vertx.createHttpServer() | |
| 49 | - .requestHandler(router) | |
| 50 | - .listen(PORT, http -> { | |
| 51 | - if (http.succeeded()) { | |
| 52 | - log.info("HTTP 服务器启动成功,端口为: {}", PORT); | |
| 53 | - } else { | |
| 54 | - log.error("HTTP 服务器启动失败", http.cause()); | |
| 55 | - } | |
| 56 | - }); | |
| 33 | + // 2. 启动 Vertx | |
| 34 | + VertxService vertxService = pluginContext.getBean(VertxService.class); | |
| 35 | + vertxService.startServer(); | |
| 57 | 36 | } |
| 58 | 37 | |
| 38 | + | |
| 59 | 39 | @Override |
| 60 | 40 | public void stop() { |
| 61 | - log.info("HttpVertxPlugin.stop()"); | |
| 62 | - if (vertx != null) { | |
| 63 | - vertx.close(ar -> { | |
| 64 | - if (ar.succeeded()) { | |
| 65 | - log.info("Vert.x 关闭成功"); | |
| 66 | - } else { | |
| 67 | - log.error("Vert.x 关闭失败", ar.cause()); | |
| 68 | - } | |
| 69 | - }); | |
| 41 | + log.info("[HttpVertxPlugin] stop ..."); | |
| 42 | + ApplicationContext pluginContext = getApplicationContext(); | |
| 43 | + if (pluginContext != null) { | |
| 44 | + // 停止服务器 | |
| 45 | + VertxService vertxService = pluginContext.getBean(VertxService.class); | |
| 46 | + vertxService.stopServer(); | |
| 70 | 47 | } |
| 71 | 48 | } |
| 72 | 49 | |
| 73 | 50 | @Override |
| 74 | 51 | protected ApplicationContext createApplicationContext() { |
| 75 | - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); | |
| 76 | - applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); | |
| 77 | - applicationContext.refresh(); | |
| 52 | + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext() { | |
| 53 | + @Override | |
| 54 | + protected void prepareRefresh() { | |
| 55 | + // 在刷新容器前注册主程序中的 Bean | |
| 56 | + ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); | |
| 57 | + DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); | |
| 58 | + beanFactory.registerSingleton("deviceDataApi", deviceDataApi); | |
| 59 | + | |
| 60 | + super.prepareRefresh(); | |
| 61 | + } | |
| 62 | + }; | |
| 78 | 63 | |
| 79 | - return applicationContext; | |
| 64 | + pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); | |
| 65 | + pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); | |
| 66 | + pluginContext.refresh(); | |
| 67 | + | |
| 68 | + return pluginContext; | |
| 80 | 69 | } |
| 81 | -} | |
| 82 | 70 | \ No newline at end of file |
| 71 | + | |
| 72 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.plugin.http.config; |
| 2 | 2 | |
| 3 | -import org.pf4j.DefaultPluginManager; | |
| 4 | -import org.pf4j.PluginWrapper; | |
| 3 | +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; | |
| 4 | +import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; | |
| 5 | +import io.vertx.core.Vertx; | |
| 6 | +import io.vertx.ext.web.Router; | |
| 7 | +import io.vertx.ext.web.handler.BodyHandler; | |
| 8 | +import lombok.extern.slf4j.Slf4j; | |
| 9 | +import org.springframework.beans.factory.annotation.Value; | |
| 5 | 10 | import org.springframework.context.annotation.Bean; |
| 6 | 11 | import org.springframework.context.annotation.Configuration; |
| 7 | 12 | |
| 13 | +/** | |
| 14 | + * 插件与独立运行都可复用的配置类 | |
| 15 | + */ | |
| 16 | +@Slf4j | |
| 8 | 17 | @Configuration |
| 9 | 18 | public class HttpVertxPluginConfiguration { |
| 10 | 19 | |
| 11 | - @Bean(initMethod = "start") | |
| 12 | - public HttpVertxPlugin httpVertxPlugin() { | |
| 13 | - PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null); | |
| 14 | - return new HttpVertxPlugin(pluginWrapper); | |
| 20 | + /** | |
| 21 | + * 可在 application.yml 中配置,默认端口 8092 | |
| 22 | + */ | |
| 23 | + @Value("${plugin.http.server.port:8092}") | |
| 24 | + private Integer port; | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 创建 Vert.x 实例 | |
| 28 | + */ | |
| 29 | + @Bean | |
| 30 | + public Vertx vertx() { | |
| 31 | + return Vertx.vertx(); | |
| 32 | + } | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * 创建路由 | |
| 36 | + */ | |
| 37 | + @Bean | |
| 38 | + public Router router(Vertx vertx, HttpVertxHandler httpVertxHandler) { | |
| 39 | + Router router = Router.router(vertx); | |
| 40 | + | |
| 41 | + // 处理 Body | |
| 42 | + router.route().handler(BodyHandler.create()); | |
| 43 | + | |
| 44 | + // 设置路由 | |
| 45 | + router.post("/sys/:productKey/:deviceName/thing/event/property/post") | |
| 46 | + .handler(httpVertxHandler); | |
| 47 | + | |
| 48 | + return router; | |
| 49 | + } | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 注入你的 Http 处理器 Handler,依赖 DeviceDataApi | |
| 53 | + */ | |
| 54 | + @Bean | |
| 55 | + public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { | |
| 56 | + return new HttpVertxHandler(deviceDataApi); | |
| 57 | + } | |
| 58 | + | |
| 59 | + /** | |
| 60 | + * 定义一个 VertxService 来管理服务器启动逻辑 | |
| 61 | + * 无论是独立运行还是插件方式,都可以共用此类 | |
| 62 | + */ | |
| 63 | + @Bean | |
| 64 | + public VertxService vertxService(Vertx vertx, Router router) { | |
| 65 | + return new VertxService(port, vertx, router); | |
| 15 | 66 | } |
| 16 | -} | |
| 17 | 67 | \ No newline at end of file |
| 68 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin.http.config; | |
| 2 | + | |
| 3 | +import io.vertx.core.Vertx; | |
| 4 | +import io.vertx.ext.web.Router; | |
| 5 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 封装 Vert.x HTTP 服务的启动/关闭逻辑 | |
| 9 | + */ | |
| 10 | +@Slf4j | |
| 11 | +public class VertxService { | |
| 12 | + | |
| 13 | + private final Integer port; | |
| 14 | + private final Vertx vertx; | |
| 15 | + private final Router router; | |
| 16 | + | |
| 17 | + public VertxService(Integer port, Vertx vertx, Router router) { | |
| 18 | + this.port = port; | |
| 19 | + this.vertx = vertx; | |
| 20 | + this.router = router; | |
| 21 | + } | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 启动 HTTP 服务器 | |
| 25 | + */ | |
| 26 | + public void startServer() { | |
| 27 | + vertx.createHttpServer() | |
| 28 | + .requestHandler(router) | |
| 29 | + .listen(port, http -> { | |
| 30 | + if (http.succeeded()) { | |
| 31 | + log.info("[VertxService] HTTP 服务器启动成功, 端口: {}", port); | |
| 32 | + } else { | |
| 33 | + log.error("[VertxService] HTTP 服务器启动失败", http.cause()); | |
| 34 | + } | |
| 35 | + }); | |
| 36 | + } | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 关闭 HTTP 服务器 | |
| 40 | + */ | |
| 41 | + public void stopServer() { | |
| 42 | + if (vertx != null) { | |
| 43 | + vertx.close(ar -> { | |
| 44 | + if (ar.succeeded()) { | |
| 45 | + log.info("[VertxService] Vert.x 关闭成功"); | |
| 46 | + } else { | |
| 47 | + log.error("[VertxService] Vert.x 关闭失败", ar.cause()); | |
| 48 | + } | |
| 49 | + }); | |
| 50 | + } | |
| 51 | + } | |
| 52 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java
| ... | ... | @@ -22,77 +22,51 @@ public class HttpVertxHandler implements Handler<RoutingContext> { |
| 22 | 22 | public void handle(RoutingContext ctx) { |
| 23 | 23 | String productKey = ctx.pathParam("productKey"); |
| 24 | 24 | String deviceName = ctx.pathParam("deviceName"); |
| 25 | - RequestBody requestBody = ctx.body(); | |
| 26 | 25 | |
| 26 | + RequestBody requestBody = ctx.body(); | |
| 27 | 27 | JSONObject jsonData; |
| 28 | 28 | try { |
| 29 | 29 | jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); |
| 30 | 30 | } catch (Exception e) { |
| 31 | - JSONObject res = createResponseJson( | |
| 32 | - 400, | |
| 33 | - new JSONObject(), | |
| 34 | - null, | |
| 35 | - "请求数据不是合法的 JSON 格式: " + e.getMessage(), | |
| 36 | - "thing.event.property.post", | |
| 37 | - "1.0"); | |
| 38 | - ctx.response() | |
| 39 | - .setStatusCode(400) | |
| 31 | + log.error("[HttpVertxHandler] 请求数据解析失败", e); | |
| 32 | + ctx.response().setStatusCode(400) | |
| 40 | 33 | .putHeader("Content-Type", "application/json; charset=UTF-8") |
| 41 | - .end(res.toString()); | |
| 34 | + .end(createResponseJson(400, null, null, | |
| 35 | + "请求数据不是合法的 JSON 格式: " + e.getMessage(), | |
| 36 | + "thing.event.property.post", "1.0").toString()); | |
| 42 | 37 | return; |
| 43 | 38 | } |
| 44 | 39 | |
| 45 | - String id = jsonData.getStr("id", null); | |
| 40 | + String id = jsonData.getStr("id"); | |
| 46 | 41 | |
| 47 | 42 | try { |
| 48 | - // 调用主程序的接口保存数据 | |
| 49 | - IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() | |
| 43 | + IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder() | |
| 50 | 44 | .productKey(productKey) |
| 51 | 45 | .deviceName(deviceName) |
| 52 | - .params(jsonData) // TODO 芋艿:这块要优化 | |
| 46 | + .params(jsonData) | |
| 53 | 47 | .build(); |
| 54 | - deviceDataApi.reportDevicePropertyData(createDTO); | |
| 55 | 48 | |
| 56 | - // 构造成功响应内容 | |
| 57 | - JSONObject successRes = createResponseJson( | |
| 58 | - 200, | |
| 59 | - new JSONObject(), | |
| 60 | - id, | |
| 61 | - "success", | |
| 62 | - "thing.event.property.post", | |
| 63 | - "1.0"); | |
| 49 | + deviceDataApi.reportDevicePropertyData(reportReqDTO); | |
| 50 | + | |
| 64 | 51 | ctx.response() |
| 65 | 52 | .setStatusCode(200) |
| 66 | 53 | .putHeader("Content-Type", "application/json; charset=UTF-8") |
| 67 | - .end(successRes.toString()); | |
| 54 | + .end(createResponseJson(200, new JSONObject(), id, "success", | |
| 55 | + "thing.event.property.post", "1.0").toString()); | |
| 56 | + | |
| 68 | 57 | } catch (Exception e) { |
| 69 | - JSONObject errorRes = createResponseJson( | |
| 70 | - 500, | |
| 71 | - new JSONObject(), | |
| 72 | - id, | |
| 73 | - "The format of result is error!", | |
| 74 | - "thing.event.property.post", | |
| 75 | - "1.0"); | |
| 58 | + log.error("[HttpVertxHandler] 上报属性数据失败", e); | |
| 76 | 59 | ctx.response() |
| 77 | 60 | .setStatusCode(500) |
| 78 | 61 | .putHeader("Content-Type", "application/json; charset=UTF-8") |
| 79 | - .end(errorRes.toString()); | |
| 62 | + .end(createResponseJson(500, new JSONObject(), id, | |
| 63 | + "The format of result is error!", | |
| 64 | + "thing.event.property.post", "1.0").toString()); | |
| 80 | 65 | } |
| 81 | 66 | } |
| 82 | 67 | |
| 83 | - /** | |
| 84 | - * 创建标准化的响应 JSON 对象 | |
| 85 | - * | |
| 86 | - * @param code 响应状态码(业务层面的) | |
| 87 | - * @param data 返回的数据对象(JSON) | |
| 88 | - * @param id 请求的 id(可选) | |
| 89 | - * @param message 返回的提示信息 | |
| 90 | - * @param method 返回的 method 标识 | |
| 91 | - * @param version 返回的版本号 | |
| 92 | - * @return 构造好的 JSON 对象 | |
| 93 | - */ | |
| 94 | - private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, | |
| 95 | - String version) { | |
| 68 | + private JSONObject createResponseJson(int code, JSONObject data, String id, | |
| 69 | + String message, String method, String version) { | |
| 96 | 70 | JSONObject res = new JSONObject(); |
| 97 | 71 | res.set("code", code); |
| 98 | 72 | res.set("data", data != null ? data : new JSONObject()); | ... | ... |