Преглед изворни кода

Merge remote-tracking branch 'origin/master' into mqy20260511

mqy20260511
lenovo пре 2 недеља
родитељ
комит
502604ea6c

+ 8
- 8
CLAUDE.md Прегледај датотеку

55
 ### TDengine (Time-Series Database)
55
 ### TDengine (Time-Series Database)
56
 - Used for high-frequency IoT telemetry ingestion
56
 - Used for high-frequency IoT telemetry ingestion
57
 - Connection pool: HikariCP via `TDengineService`
57
 - Connection pool: HikariCP via `TDengineService`
58
-- Connects to `jdbc:TAOS://localhost:6030/`
58
+- Connects to `jdbc:TAOS://127.0.0.1:6031/`
59
 - Super-table pattern with column caching (`stableColumnCache`)
59
 - Super-table pattern with column caching (`stableColumnCache`)
60
 
60
 
61
 ### Redis
61
 ### Redis
71
 - `iot-platform/src/main/resources/application.yml` — Main config (port 8887, Redis, MyBatis, Actuator)
71
 - `iot-platform/src/main/resources/application.yml` — Main config (port 8887, Redis, MyBatis, Actuator)
72
 - `iot-platform/src/main/resources/application-druid.yml` — Database connection (Druid pool, master/slave)
72
 - `iot-platform/src/main/resources/application-druid.yml` — Database connection (Druid pool, master/slave)
73
 - `iot-platform/src/main/resources/logback-spring.xml` — Logging config with rolling
73
 - `iot-platform/src/main/resources/logback-spring.xml` — Logging config with rolling
74
-- `.env` — Environment variables (passwords, credentials) loaded by systemd or start.sh
74
+- `.env` — Environment variables (passwords, credentials) loaded by start-container.sh
75
 
75
 
76
 ## Environment Variables
76
 ## Environment Variables
77
 
77
 
134
 
134
 
135
 ## Deployment
135
 ## Deployment
136
 
136
 
137
-Production deployment uses systemd on online180 (47.104.204.180).
137
+Production deployment uses Podman containers on online180 (47.104.204.180).
138
 
138
 
139
 ```bash
139
 ```bash
140
 # Deploy from local
140
 # Deploy from local
141
 cd deploy
141
 cd deploy
142
-./deploy.sh --build          # Build and deploy
142
+./deploy.sh --build          # Build, build image, and deploy
143
 ./deploy.sh --jar <path>     # Deploy specific jar
143
 ./deploy.sh --jar <path>     # Deploy specific jar
144
 
144
 
145
-# Server management
146
-systemctl status iot-platform
147
-systemctl restart iot-platform
148
-journalctl -u iot-platform -f
145
+# Container management
146
+podman ps | grep iot-platform
147
+podman logs -f iot-platform
148
+podman restart iot-platform
149
 ```
149
 ```
150
 
150
 
151
 See `deploy/README.md` for full deployment documentation.
151
 See `deploy/README.md` for full deployment documentation.

+ 57
- 26
deploy/README.md Прегледај датотеку

2
 
2
 
3
 ## 概述
3
 ## 概述
4
 
4
 
5
-本项目使用 systemd 管理进程,通过 `deploy.sh` 脚本实现一键构建、上传、备份、健康检查和自动回滚。
5
+本项目使用 Podman 容器化部署,通过 `deploy.sh` 脚本实现一键构建、上传、构建镜像、健康检查和自动回滚。
6
 
6
 
7
 ## 服务器环境
7
 ## 服务器环境
8
 
8
 
9
 - **服务器**: online180 (47.104.204.180)
9
 - **服务器**: online180 (47.104.204.180)
10
 - **OS**: Alibaba Cloud Linux 3
10
 - **OS**: Alibaba Cloud Linux 3
11
-- **Java**: OpenJDK 1.8.0_412
11
+- **容器运行时**: Podman 4.9.4 (docker 兼容)
12
+- **Java**: OpenJDK 1.8 (容器内)
12
 - **安装目录**: `/opt/iot-platform/`
13
 - **安装目录**: `/opt/iot-platform/`
13
-- **服务名称**: `iot-platform`
14
+- **容器名称**: `iot-platform`
14
 
15
 
15
 ## 目录结构
16
 ## 目录结构
16
 
17
 
17
 ```
18
 ```
18
 /opt/iot-platform/
19
 /opt/iot-platform/
19
 ├── iot-platform.jar          # 当前运行版本
20
 ├── iot-platform.jar          # 当前运行版本
21
+├── Dockerfile                # 容器镜像构建定义
22
+├── start-container.sh        # 容器启动脚本(加载 .env)
20
 ├── .env                      # 环境变量(密码、凭据)
23
 ├── .env                      # 环境变量(密码、凭据)
21
 ├── backup/                   # 版本备份(自动创建)
24
 ├── backup/                   # 版本备份(自动创建)
22
 ├── bin/
25
 ├── bin/
23
 │   ├── deploy.sh             # 一键部署脚本(本地运行)
26
 │   ├── deploy.sh             # 一键部署脚本(本地运行)
24
 │   └── health-check.sh       # 健康检查脚本
27
 │   └── health-check.sh       # 健康检查脚本
25
 ├── config/                   # 线上专属配置
28
 ├── config/                   # 线上专属配置
26
-└── logs/                     # 日志输出
29
+└── logs/                     # 日志输出(宿主机挂载)
27
 ```
30
 ```
28
 
31
 
29
 ## 快速开始
32
 ## 快速开始
30
 
33
 
31
 ### 首次部署(服务器初始化)
34
 ### 首次部署(服务器初始化)
32
 
35
 
33
-如果服务器上还没有安装 systemd 服务,执行初始化:
36
+确保服务器上已有 `/opt/iot-platform/` 目录,并包含 `Dockerfile`、`.env`、`start-container.sh`:
37
+
38
+```bash
39
+# 从本地同步必要文件到服务器
40
+scp deploy/setup-server.sh root@47.104.204.180:/opt/iot-platform/
41
+scp .env root@47.104.204.180:/opt/iot-platform/
42
+scp Dockerfile root@47.104.204.180:/opt/iot-platform/
43
+scp start-container.sh root@47.104.204.180:/opt/iot-platform/
44
+ssh root@47.104.204.180 "chmod +x /opt/iot-platform/start-container.sh"
45
+```
46
+
47
+然后执行首次部署:
34
 
48
 
35
 ```bash
49
 ```bash
36
 cd deploy
50
 cd deploy
37
-scp setup-server.sh root@47.104.204.180:/tmp/
38
-scp iot-platform.service root@47.104.204.180:/tmp/
39
-scp ../.env root@47.104.204.180:/tmp/iot-platform.env
40
-ssh root@47.104.204.180 "chmod +x /tmp/setup-server.sh && /tmp/setup-server.sh"
51
+./deploy.sh --build
41
 ```
52
 ```
42
 
53
 
43
 ### 日常部署
54
 ### 日常部署
53
 1. 本地执行 `mvn clean package`
64
 1. 本地执行 `mvn clean package`
54
 2. 上传 jar 到服务器
65
 2. 上传 jar 到服务器
55
 3. 备份当前版本(带时间戳)
66
 3. 备份当前版本(带时间戳)
56
-4. 停止 systemd 服务
57
-5. 替换 jar
58
-6. 启动服务
67
+4. 停止并删除旧容器
68
+5. 替换 jar 并构建新镜像
69
+6. 启动容器(host 网络模式)
59
 7. 健康检查(`/actuator/health`)
70
 7. 健康检查(`/actuator/health`)
60
 8. 失败则自动回滚到上一个版本
71
 8. 失败则自动回滚到上一个版本
61
 
72
 
104
 ## 服务器管理
115
 ## 服务器管理
105
 
116
 
106
 ```bash
117
 ```bash
107
-# 查看状态
108
-systemctl status iot-platform
118
+# 查看容器状态
119
+podman ps | grep iot-platform
109
 
120
 
110
-# 重启
111
-systemctl restart iot-platform
121
+# 查看实时日志
122
+podman logs -f iot-platform
112
 
123
 
113
-# 停止
114
-systemctl stop iot-platform
124
+# 查看最后 100 行日志
125
+podman logs --tail 100 iot-platform
115
 
126
 
116
-# 查看日志
117
-journalctl -u iot-platform -f
127
+# 查看宿主机上的日志文件(和容器内同步)
128
+tail -f /opt/iot-platform/logs/iot-platform.$(date +%Y-%m-%d).*.log
118
 
129
 
119
-# 查看历史日志
120
-journalctl -u iot-platform --since "1 hour ago"
130
+# 停止容器
131
+podman stop iot-platform
132
+
133
+# 删除容器
134
+podman rm iot-platform
135
+
136
+# 重启容器
137
+podman restart iot-platform
121
 ```
138
 ```
122
 
139
 
123
 ## 健康检查
140
 ## 健康检查
142
 
159
 
143
 ```bash
160
 ```bash
144
 ssh root@47.104.204.180
161
 ssh root@47.104.204.180
145
-systemctl stop iot-platform
162
+
163
+# 停止并删除当前容器
164
+podman stop iot-platform && podman rm iot-platform
165
+
166
+# 恢复旧版本 jar
146
 ls /opt/iot-platform/backup/          # 查看可用备份
167
 ls /opt/iot-platform/backup/          # 查看可用备份
147
 cp /opt/iot-platform/backup/iot-platform-XXXX.jar /opt/iot-platform/iot-platform.jar
168
 cp /opt/iot-platform/backup/iot-platform-XXXX.jar /opt/iot-platform/iot-platform.jar
148
-systemctl start iot-platform
169
+
170
+# 重新构建镜像并启动
171
+cd /opt/iot-platform && podman build -t iot-platform:latest .
172
+podman run -d \
173
+    --name iot-platform \
174
+    --network host \
175
+    --restart unless-stopped \
176
+    -v /opt/iot-platform/logs:/opt/iot-platform/logs \
177
+    localhost/iot-platform:latest
149
 ```
178
 ```
150
 
179
 
151
 ## 配置文件说明
180
 ## 配置文件说明
153
 | 文件 | 说明 |
182
 | 文件 | 说明 |
154
 |------|------|
183
 |------|------|
155
 | `deploy.sh` | 一键部署脚本(本地执行) |
184
 | `deploy.sh` | 一键部署脚本(本地执行) |
156
-| `setup-server.sh` | 服务器初始化脚本(服务器执行) |
185
+| `setup-server.sh` | 服务器初始化脚本 |
157
 | `health-check.sh` | 健康检查脚本 |
186
 | `health-check.sh` | 健康检查脚本 |
158
-| `iot-platform.service` | systemd 服务定义 |
187
+| `Dockerfile` | 容器镜像构建定义 |
188
+| `start-container.sh` | 容器内启动脚本(加载 .env) |
189
+| `iot-platform.service` | systemd 服务定义(已废弃,保留备用) |

+ 40
- 20
deploy/deploy.sh Прегледај датотеку

1
 #!/bin/bash
1
 #!/bin/bash
2
-# IoT Platform 一键部署脚本
2
+# IoT Platform 容器化一键部署脚本 (Podman)
3
 # 用法:
3
 # 用法:
4
 #   ./deploy.sh --build              # 本地构建后部署
4
 #   ./deploy.sh --build              # 本地构建后部署
5
 #   ./deploy.sh --jar path/to/jar    # 使用指定 jar 部署
5
 #   ./deploy.sh --jar path/to/jar    # 使用指定 jar 部署
107
 "
107
 "
108
 echo -e "${GREEN}[2/6] 备份完成${NC}"
108
 echo -e "${GREEN}[2/6] 备份完成${NC}"
109
 
109
 
110
-# 步骤 3: 停止服务
111
-echo -e "${YELLOW}[3/6] 停止服务...${NC}"
112
-ssh "${SERVER_USER}@${SERVER_HOST}" "systemctl stop ${APP_NAME} || true"
110
+# 步骤 3: 停止旧容器
111
+echo -e "${YELLOW}[3/6] 停止旧容器...${NC}"
112
+ssh "${SERVER_USER}@${SERVER_HOST}" "podman stop ${APP_NAME} >/dev/null 2>&1 && podman rm ${APP_NAME} >/dev/null 2>&1 || true"
113
 sleep 2
113
 sleep 2
114
-echo -e "${GREEN}[3/6] 服务已停止${NC}"
114
+echo -e "${GREEN}[3/6] 旧容器已清理${NC}"
115
 
115
 
116
-# 步骤 4: 替换 JAR
117
-echo -e "${YELLOW}[4/6] 替换 JAR...${NC}"
116
+# 步骤 4: 替换 JAR 并构建镜像
117
+echo -e "${YELLOW}[4/6] 替换 JAR 并构建镜像...${NC}"
118
 ssh "${SERVER_USER}@${SERVER_HOST}" "
118
 ssh "${SERVER_USER}@${SERVER_HOST}" "
119
     cp ${REMOTE_DIR}/tmp/${JAR_NAME} ${REMOTE_DIR}/${APP_NAME}.jar
119
     cp ${REMOTE_DIR}/tmp/${JAR_NAME} ${REMOTE_DIR}/${APP_NAME}.jar
120
     chmod 644 ${REMOTE_DIR}/${APP_NAME}.jar
120
     chmod 644 ${REMOTE_DIR}/${APP_NAME}.jar
121
     rm -f ${REMOTE_DIR}/tmp/${JAR_NAME}
121
     rm -f ${REMOTE_DIR}/tmp/${JAR_NAME}
122
     echo '  新 JAR:'
122
     echo '  新 JAR:'
123
     ls -lh ${REMOTE_DIR}/${APP_NAME}.jar
123
     ls -lh ${REMOTE_DIR}/${APP_NAME}.jar
124
+    echo '  构建镜像...'
125
+    cd ${REMOTE_DIR} && podman build -t ${APP_NAME}:latest . >/dev/null 2>&1
126
+    echo '  镜像构建完成'
127
+    podman images | grep ${APP_NAME}
124
 "
128
 "
125
-echo -e "${GREEN}[4/6] 替换完成${NC}"
129
+echo -e "${GREEN}[4/6] 镜像构建完成${NC}"
126
 
130
 
127
-# 步骤 5: 启动服务
128
-echo -e "${YELLOW}[5/6] 启动服务...${NC}"
129
-ssh "${SERVER_USER}@${SERVER_HOST}" "systemctl start ${APP_NAME}"
130
-sleep 3
131
+# 步骤 5: 启动容器
132
+echo -e "${YELLOW}[5/6] 启动容器...${NC}"
133
+ssh "${SERVER_USER}@${SERVER_HOST}" "
134
+    podman run -d \
135
+        --name ${APP_NAME} \
136
+        --network host \
137
+        --restart unless-stopped \
138
+        -v ${REMOTE_DIR}/logs:/opt/iot-platform/logs \
139
+        localhost/${APP_NAME}:latest >/dev/null 2>&1
140
+"
141
+sleep 5
131
 
142
 
132
-echo -e "${GREEN}[5/6] 服务已启动${NC}"
143
+echo -e "${GREEN}[5/6] 容器已启动${NC}"
133
 
144
 
134
 # 步骤 6: 健康检查
145
 # 步骤 6: 健康检查
135
 echo -e "${YELLOW}[6/6] 健康检查...${NC}"
146
 echo -e "${YELLOW}[6/6] 健康检查...${NC}"
144
     echo "备份:     ${BACKUP_NAME}"
155
     echo "备份:     ${BACKUP_NAME}"
145
     echo "状态:     成功"
156
     echo "状态:     成功"
146
     echo ""
157
     echo ""
147
-    echo "服务状态:"
148
-    ssh "${SERVER_USER}@${SERVER_HOST}" "systemctl status ${APP_NAME} --no-pager"
158
+    echo "容器状态:"
159
+    ssh "${SERVER_USER}@${SERVER_HOST}" "podman ps | grep ${APP_NAME}"
160
+    echo ""
161
+    echo "查看日志:"
162
+    echo "  ssh ${SERVER_USER}@${SERVER_HOST} 'podman logs -f ${APP_NAME}'"
149
     exit 0
163
     exit 0
150
 else
164
 else
151
     echo -e "${RED}[6/6] 健康检查失败!${NC}"
165
     echo -e "${RED}[6/6] 健康检查失败!${NC}"
153
     if [ "$NO_ROLLBACK" = true ]; then
167
     if [ "$NO_ROLLBACK" = true ]; then
154
         echo -e "${YELLOW}[no-rollback] 已启用不回滚模式,保留新版本用于排查${NC}"
168
         echo -e "${YELLOW}[no-rollback] 已启用不回滚模式,保留新版本用于排查${NC}"
155
         echo -e "${YELLOW}排查命令:${NC}"
169
         echo -e "${YELLOW}排查命令:${NC}"
156
-        echo "  ssh ${SERVER_USER}@${SERVER_HOST} 'journalctl -u ${APP_NAME} -f'"
157
-        echo "  ssh ${SERVER_USER}@${SERVER_HOST} 'systemctl status ${APP_NAME}'"
170
+        echo "  ssh ${SERVER_USER}@${SERVER_HOST} 'podman logs -f ${APP_NAME}'"
171
+        echo "  ssh ${SERVER_USER}@${SERVER_HOST} 'podman ps | grep ${APP_NAME}'"
158
         exit 1
172
         exit 1
159
     fi
173
     fi
160
 
174
 
161
     echo -e "${YELLOW}[rollback] 执行回滚...${NC}"
175
     echo -e "${YELLOW}[rollback] 执行回滚...${NC}"
162
-    ssh "${SERVER_USER}@${SERVER_HOST}" "systemctl stop ${APP_NAME} || true"
176
+    ssh "${SERVER_USER}@${SERVER_HOST}" "podman stop ${APP_NAME} >/dev/null 2>&1 && podman rm ${APP_NAME} >/dev/null 2>&1 || true"
163
 
177
 
164
     if ssh "${SERVER_USER}@${SERVER_HOST}" "test -f ${REMOTE_DIR}/backup/${BACKUP_NAME}"; then
178
     if ssh "${SERVER_USER}@${SERVER_HOST}" "test -f ${REMOTE_DIR}/backup/${BACKUP_NAME}"; then
165
         echo -e "${YELLOW}[rollback] 恢复旧版本...${NC}"
179
         echo -e "${YELLOW}[rollback] 恢复旧版本...${NC}"
166
         ssh "${SERVER_USER}@${SERVER_HOST}" "
180
         ssh "${SERVER_USER}@${SERVER_HOST}" "
167
             cp ${REMOTE_DIR}/backup/${BACKUP_NAME} ${REMOTE_DIR}/${APP_NAME}.jar
181
             cp ${REMOTE_DIR}/backup/${BACKUP_NAME} ${REMOTE_DIR}/${APP_NAME}.jar
168
-            systemctl start ${APP_NAME}
182
+            cd ${REMOTE_DIR} && podman build -t ${APP_NAME}:latest . >/dev/null 2>&1
183
+            podman run -d \
184
+                --name ${APP_NAME} \
185
+                --network host \
186
+                --restart unless-stopped \
187
+                -v ${REMOTE_DIR}/logs:/opt/iot-platform/logs \
188
+                localhost/${APP_NAME}:latest >/dev/null 2>&1
169
         "
189
         "
170
-        sleep 3
190
+        sleep 5
171
 
191
 
172
         if ssh "${SERVER_USER}@${SERVER_HOST}" "bash ${REMOTE_DIR}/bin/health-check.sh localhost 8887 60"; then
192
         if ssh "${SERVER_USER}@${SERVER_HOST}" "bash ${REMOTE_DIR}/bin/health-check.sh localhost 8887 60"; then
173
             echo -e "${GREEN}[rollback] 回滚成功,旧版本已恢复${NC}"
193
             echo -e "${GREEN}[rollback] 回滚成功,旧版本已恢复${NC}"

+ 1
- 1
iot-platform/pom.xml Прегледај датотеку

74
         <dependency>
74
         <dependency>
75
             <groupId>com.taosdata.jdbc</groupId>
75
             <groupId>com.taosdata.jdbc</groupId>
76
             <artifactId>taos-jdbcdriver</artifactId>
76
             <artifactId>taos-jdbcdriver</artifactId>
77
-            <version>3.2.7</version>
77
+            <version>3.3.2</version>
78
         </dependency>
78
         </dependency>
79
 
79
 
80
         <!-- HikariCP (TdEngine 连接池) -->
80
         <!-- HikariCP (TdEngine 连接池) -->

+ 1
- 1
iot-platform/src/main/java/com/iot/platform/config/IotProperties.java Прегледај датотеку

115
      * TDengine配置
115
      * TDengine配置
116
      */
116
      */
117
     public static class TDengine {
117
     public static class TDengine {
118
-        private String url = "jdbc:TAOS://localhost:6030/";
118
+        private String url = "jdbc:TAOS://127.0.0.1:6031/";
119
         private String username = "";
119
         private String username = "";
120
         private String password = "";
120
         private String password = "";
121
 
121
 

+ 3
- 2
iot-platform/src/main/java/com/iot/platform/service/TdEngineService.java Прегледај датотеку

620
     /**
620
     /**
621
      * 确保表存在
621
      * 确保表存在
622
      */
622
      */
623
-    private void ensureTableExists(String dbName, String superTableName, String table) throws SQLException {
623
+    private void ensureTableExists(String dbName, String superTableName, String table)
624
+            throws SQLException {
625
+
624
         if (!isValidTableName(dbName) || !isValidTableName(superTableName) || !isValidTableName(table)) {
626
         if (!isValidTableName(dbName) || !isValidTableName(superTableName) || !isValidTableName(table)) {
625
             throw new IllegalArgumentException("Invalid database or table name: dbName=" + dbName
627
             throw new IllegalArgumentException("Invalid database or table name: dbName=" + dbName
626
                     + ", superTableName=" + superTableName + ", table=" + table);
628
                     + ", superTableName=" + superTableName + ", table=" + table);
687
             dataSource.close();
689
             dataSource.close();
688
         }
690
         }
689
     }
691
     }
690
-
691
 }
692
 }

+ 1
- 1
iot-platform/src/main/resources/application.yml Прегледај датотеку

70
     password: ${MQTT_PASSWORD:}
70
     password: ${MQTT_PASSWORD:}
71
     charge-station-topic: ${MQTT_CHARGE_STATION_TOPIC:station/ChargeStation/device/+/post/json}
71
     charge-station-topic: ${MQTT_CHARGE_STATION_TOPIC:station/ChargeStation/device/+/post/json}
72
   tdengine:
72
   tdengine:
73
-    url: jdbc:TAOS://localhost:6030/
73
+    url: jdbc:TAOS://127.0.0.1:6031/
74
     username: ${TDENGINE_USERNAME:}
74
     username: ${TDENGINE_USERNAME:}
75
     password: ${TDENGINE_PASSWORD:}
75
     password: ${TDENGINE_PASSWORD:}

+ 2
- 2
iot-platform/src/test/java/com/iot/platform/service/TDengineServiceTest.java Прегледај датотеку

38
     @BeforeEach
38
     @BeforeEach
39
     void setUp() {
39
     void setUp() {
40
         when(iotProperties.getTdengine()).thenReturn(tdengineConfig);
40
         when(iotProperties.getTdengine()).thenReturn(tdengineConfig);
41
-        when(tdengineConfig.getUrl()).thenReturn("jdbc:TAOS://localhost:6030/test");
41
+        when(tdengineConfig.getUrl()).thenReturn("jdbc:TAOS://localhost:6031/test");
42
         when(tdengineConfig.getUsername()).thenReturn("root");
42
         when(tdengineConfig.getUsername()).thenReturn("root");
43
         when(tdengineConfig.getPassword()).thenReturn("taosdata");
43
         when(tdengineConfig.getPassword()).thenReturn("taosdata");
44
     }
44
     }
330
     @Test
330
     @Test
331
     @DisplayName("getConnection: 数据源初始化失败时应抛异常")
331
     @DisplayName("getConnection: 数据源初始化失败时应抛异常")
332
     void getConnection_initFails_throwsException() {
332
     void getConnection_initFails_throwsException() {
333
-        when(tdengineConfig.getUrl()).thenReturn("jdbc:TAOS://invalid:6030/test");
333
+        when(tdengineConfig.getUrl()).thenReturn("jdbc:TAOS://invalid:6031/test");
334
 
334
 
335
         assertThatThrownBy(() -> tdengineService.getConnection())
335
         assertThatThrownBy(() -> tdengineService.getConnection())
336
                 .isInstanceOf(Throwable.class);
336
                 .isInstanceOf(Throwable.class);

Loading…
Откажи
Сачувај