plantpulse-server(Web控制台 + IIoT引擎)
角色
这是平台的门面与大脑——核心模块。操作员看到的Web控制台、外部系统调用的REST API,以及实际处理工业数据的IIoT引擎,全部在这个模块内运行。
| 项目 | 值 |
|---|---|
| 模块名 | plantpulse-server |
| 容器 | plantpulse-server-web(自身镜像,自身容器) |
| 容器内路径 | /opt/kopens/plantpulse-platform/plantpulse-server/ |
| 用户接入 | 80/443 — 由 plantpulse-proxy 接收并转发到该容器。管理控制台也使用同一地址 |
| 主机端口 | 无 — 仅在代理后运行 |
| 内存限制 | DOCKER_SERVER_MEMORY 默认 14g / 堆 DOCKER_SERVER_HEAP 默认 8g |
| 运行时 | Apache Tomcat 10.1 + Java 21 |
| 吞吐量 | 每秒40,000条消息(36线程管道) |
用户流量始终经由 plantpulse-proxy(80/443)。因此即使后端迁移或改名,也无需修改用户和设备配置。容器状态通过 bin/status.sh 查看,日志通过 bin/logs.sh plantpulse-server-web 查看。
职责范围
目录结构
plantpulse-server/
├── config/ # 서버 관리 설정 (외부화 — 배포해도 유지)
│ ├── plantpulse-engine.properties # IIoT 엔진 튜닝
│ ├── plantpulse-storage.properties # DB 연결
│ ├── plantpulse-mq.properties # 메시징 연결
│ └── log4j2.xml # 로깅 설정 (자가 시딩 — 최초 기동 시 생성)
├── bin/
│ ├── start.sh
│ ├── stop.sh
│ └── log-viewer.sh
├── logs/
│ └── system.log
├── path/ # 경로 매핑
└── server/ # 내장 Tomcat
├── conf/
│ ├── server.xml # 커넥터 / 포트
│ ├── context.xml # 컨텍스트 / 데이터소스
│ ├── catalina.properties
│ ├── logging.properties
│ ├── tomcat-users.xml
│ ├── web.xml
│ └── Catalina/localhost/
│ └── ROOT.xml # 컨텍스트 설정 (sessionCookieName 등)
└── webapps/
├── ROOT.war # 웹 애플리케이션 (WAR 통배포)
└── ROOT/ # Tomcat 이 unpack 한 디렉토리 (수정 금지)
部署方式:Web应用采用
server/webapps/ROOT.war替换方式部署(WAR 整体部署,正在转型中)。现有的app/plantpulse-server-webexploded 目录方式计划废弃,并且server.xml的 ContextdocBase覆盖已移除。直接修改ROOT/内部的文件会在下次部署时丢失,因此配置变更请始终在config/中进行。
主要配置文件
配置文件位于模块的 config/ 目录中,服务器按 -Dpp.conf.dir → ${catalina.base}/../config → 类路径(fallback)顺序搜索。当外部配置被加载时,启动日志会输出 Properties loaded from external conf:。
application.properties 已移除(2026.06)。主题/主页等控制台运行配置在控制台的系统 > 设置管理(
mm_config表)中管理,alarm.duplicate.check.minutes已迁移至plantpulse-engine.properties。
plantpulse-engine.properties
IIoT引擎管道调优。
| 项目 | 默认值 | 说明 |
|---|---|---|
engine.pipeline.threads | 36 | 并行处理的线程数 |
engine.pipeline.ratelimit | 40000 | 每秒最大消息数 |
engine.pipeline.batch.size | 1000 | 批处理大小 |
engine.pipeline.queue.size | 1200000 | 队列容量 |
engine.dedup.enabled | true | 启用去重 |
plantpulse-storage.properties
数据存储连接。
# Cassandra
cassandra.host=${PP_CASSANDRA_HOST}
cassandra.port=${PP_CASSANDRA_PORT}
cassandra.keyspace=${PP_KEYSPACE}
cassandra.user=${PP_CASSANDRA_USER}
cassandra.password=${PP_CASSANDRA_PASSWORD}
# PostgreSQL (Spring DataSource)
postgres.url=jdbc:postgresql://${PP_POSTGRES_HOST}:${PP_POSTGRES_PORT}/${PP_DB_NAME}
postgres.user=${PP_PG_USER}
postgres.password=${PP_PG_PASSWORD}
# Valkey
redis.host=${PP_REDIS_HOST}
redis.port=${PP_REDIS_PORT}
redis.password=${PP_REDIS_PASSWORD}
此应用不渲染配置。 拆分至容器后,Web服务器无需数据湖的配置渲染器即可启动,并从三处读取值 —— ① 操作员放置的外部文件 → ② WAR 内置的默认值 → ③ compose 传递的环境变量。其中环境变量会覆盖内置默认值。
因此,应用所看到值的唯一定义处是
compose/docker-compose.yml。仅存在于数据湖、但不在 compose 中的名称不会到达此应用——曾在仅修改数据湖标识符的站点中,五个应用继续使用旧名称的事故就是一个例子(2026-08-29)。请勿修改 Web应用内部(
server/webapps/ROOT/)的文件——部署时会丢失。
server/conf/server.xml
Tomcat 连接器配置。端口更改/TLS/压缩/线程池。
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="500"
acceptCount="200"
URIEncoding="UTF-8"
compression="on"
compressibleMimeType="text/html,text/css,application/json,application/javascript" />
<Connector port="443" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
SSLEnabled="true"
maxThreads="500"
sslEnabledProtocols="TLSv1.3,TLSv1.2">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/var/security/plantpulse/server.jks"
certificateKeystorePassword="${PP_TLS_KEYSTORE_PASSWORD}" />
</SSLHostConfig>
</Connector>
运维命令
在主机上——以容器为单位
重新启动容器即为此模块的重启。基础设施和其他应用不受影响。
cd /opt/kopens/plantpulse-platform-docker
# 서버만 재시작
docker compose -f compose/docker-compose.yml restart plantpulse-server-web
# 상태 · 로그
bin/status.sh
bin/logs.sh plantpulse-server-web -n 200
进入容器内部
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
restart-server.sh 重启该脚本是一个薄委托,用于在每个应用的自身容器内调用该模块的启动器。数据湖容器中没有 Web 服务器可执行文件,因此会以«模块不存在»错误结束。请使用上述容器级别的重启。
热加载(无需重启即可应用配置)
部分配置可在控制台的系统 > 设置管理菜单中进行热加载。config/ 的属性文件或 server.xml 等启动时配置需要重启。
日志
| 日志 | 路径 | 内容 |
|---|---|---|
| 主日志 | logs/system.log | 应用逻辑、错误 |
| Tomcat catalina | server/logs/catalina.out | Tomcat 标准输出 |
| 访问日志 | server/logs/localhost_access_log.YYYY-MM-DD.txt | HTTP 请求日志 |
| 引擎日志 | logs/engine.log | IIoT 引擎处理详情 |
| 慢查询 | logs/slow.log | 超过200ms的查询 |
日志级别变更——日志配置(log4j2.xml)已外部化至模块的 config/(自引导:首次启动时,WAR 默认值被复制到 config/log4j2.xml,应用时启动日志输出 Logging reconfigured from external conf:):
# 웹 서버 컨테이너 안에서
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
vi /opt/kopens/plantpulse-platform/plantpulse-server/config/log4j2.xml
# <Logger name="plantpulse" level="DEBUG"/> 등 수정 후 exit
# 호스트에서 그 컨테이너만 재시작
cd /opt/kopens/plantpulse-platform-docker
docker compose -f compose/docker-compose.yml restart plantpulse-server-web
由于是外部文件,WAR 重新部署后也会持久保存。请勿修改 Web应用内部(
server/webapps/ROOT/WEB-INF/classes/log4j2.xml)——部署时会丢失。
性能调优
JVM 堆
# server/bin/setenv.sh (없으면 생성)
export CATALINA_OPTS="-Xms16g -Xmx32g \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/plantpulse/heap/ \
-Xlog:gc*:file=/var/log/plantpulse/gc.log:time,uptime:filecount=10,filesize=100M"
Tomcat 线程池
在 server.xml 的 Connector 中调整 maxThreads / acceptCount。在大规模并发环境下,建议如下。
| 并发连接数 | maxThreads | acceptCount |
|---|---|---|
| ~ 100 | 200 | 100 |
| ~ 500 | 500 | 200 |
| 500+ | 1000 | 500 |
IIoT 引擎吞吐量
将 plantpulse-engine.properties 的 engine.pipeline.threads 设置为主机核心数的 70~80%。当消息积压时,同时调高 engine.pipeline.queue.capacity。
更详细的调优请参考性能调优页面。
上传内容存放在哪里 —— RustFS
设备文档·设备图像·个人资料图像存储在**对象存储(RustFS)**中。数据库中仅存储 s3://<버킷>/<key> 而非本体。
| 项目 | 值 |
|---|---|
| 桶 | PP_S3_UPLOAD_BUCKET(默认 upload)— 与数据湖使用的 plantpulse 桶不同 |
| 连接 | PP_S3_HOST · PP_S3_PORT · PP_S3_USER · PP_S3_PASSWORD |
| 存放位置 | 仅在 plantpulse-server-web。不会进入工作器 |
分开桶的原因是为了防止一方的保留规则影响另一方。
2026.09 不会在启动时创建桶。 指向不存在的桶会导致上传失败。全新安装会在迁移过程中创建桶,但如果手动修改值指向其他桶,请先创建该桶。
如果连接值为空,则无法创建对象存储,上传路径也不会显示。容器环境在创建时固定,因此如果添加了值,需要重新创建容器而不是 docker restart 才能生效。
上传限制——无法用一行概述
限制有两层,实际最先触发的基本总是下层(端点)。
端点保护——先触发
| 上传内容 | 端点 | 限制 |
|---|---|---|
| 设备文档 | POST /asset/tree/document/upload | 50 MiB |
| 设备文档 (API v5) | POST /api/v5/assets/{asset_id}/documents | 50 MiB |
| 设备图像 | POST /asset/image/upload | 10 MiB |
| 画布 SVG | POST /canvas/svgupload | 10 MiB |
设备图像和画布 SVG 会在 10 MiB 处被拒绝。只有文档是 50 MiB。
超出限制时显示的内容
四个端点使用相同的验证函数,消息仅显示字节数字。
file_size: 12582912 > max 10485760
以下是上传 12 MiB 图像时的示例——10485760 为 10 MiB,52428800 为 50 MiB。
消息中没有单位,操作员必须心算字节,也不显示被阻止的文件是哪个。如果一次上传多个文件被拒绝,需要直接测量大小来查找。
容器限制——其上层的天花板
| 项目 | 值 | 位置 |
|---|---|---|
| 单个文件 | 50 MiB | web.xml 的 <max-file-size> |
| 整个 multipart 请求 | 52 MiB | web.xml 的 <max-request-size> |
| 表单参数(文件部分除外) | 2 MiB | Tomcat 默认值——两个连接器均未声明 maxPostSize |
maxPostSize 是 Tomcat 转换为请求参数的字节的限制。x-www-form-urlencoded 计算整个正文,multipart/form-data 仅统计非文件部分。JSON 等原始正文不受影响。
实测(2026-09-18):通过代理发送 3 MiB multipart 时,正文全部传输,到达应用认证层(因不是大小而是 401 而结束)。传输路径上没有 2 MiB 墙,nginx 是 client_max_body_size 200m。
在 2026-09-18 之前,只有 443 连接器有 maxPostSize="-1",因此同一服务器根据端口强制不同限制。现在 80 · 443 相同。
健康检查
# 표준 헬스체크 (익명, readiness — 엔진이 전 단계 기동을 완료(RUNNING)했을 때만 UP. 배포 자동검증 / 모니터링용)
curl -fsS http://127.0.0.1/api/health
# 준비 완료: 200 {"status":"UP","service":"plantpulse-server-web","ts":1765500000000,"checks":{"engine":"RUNNING"}}
# 기동 중: 503 {"status":"STARTING",...} / 기동 실패·중지: 503 {"status":"DEGRADED",...}
# 경로는 /api/health 다 — «/api/v5/health» 는 없다. 인증도 필요 없다(익명 허용 경로).
# 콘솔 ping
curl -fsS http://127.0.0.1/api/v5/ping
# 공개 상태 페이지의 데이터 (익명 — 24시간 가용성·p99·엔드포인트별 상태)
curl -fsS http://127.0.0.1/api/v5/public/status
# WebSocket 핸드셰이크 확인
curl -i -N \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
-H "Sec-WebSocket-Version: 13" \
http://127.0.0.1:8000/ws
常见问题
| 症状 | 原因 | 措施 |
|---|---|---|
OutOfMemoryError | 堆不足 | 在 setenv.sh 中调高 -Xmx + 分析 GC 日志 |
Too many open files | 文件描述符限制 | 将 /etc/security/limits.conf 的 nofile 设置为 65535+ |
| 控制台响应慢 | 数据库慢查询 | 检查 PostgreSQL 慢查询日志、Valkey 缓存命中率 |
| 登录无限循环 | 会话/Cookie 问题 | 清除浏览器缓存,检查 server/conf/Catalina/localhost/ROOT.xml 的 sessionCookieName |
| 502 / 504(反向代理) | 后端响应超时 | 调高 nginx proxy_read_timeout |
| WebSocket 断开 | 防火墙空闲超时 | 防火墙 timeout > 60s,调整 tomcat.websocket.session.timeout |
详细诊断请参考问题排查页面。
安全清单
- 更改默认 admin 密码(
admin / admin123!) - 移除
tomcat-users.xml的 manager 账户或设置强密码 - 阻止外部访问
server.xml的shutdown port (7000) - 应用 HTTPS 证书(
plantpulse-certs烘焙的pp-security卷中的材料或外部 CA) - 确认设置了 X-Frame-Options / CSP / HSTS 头
- 将 API Bearer Token 存储在运维 vault 中
- 禁用
config/属性文件中的调试选项并限制权限(chmod 600)
更详细的设置请参考安全设置页面。