# macOS launchctl 本机部署模板 本目录用于把本机生产验证服务的启动方式仓库化。模板面向当前生产验证链路: - `gb32960-ingest-app`: TCP `32960`,HTTP `20100` - `jt808-ingest-app`: TCP `808`,HTTP `20400` - `yutong-mqtt-app`: HTTP `20500` - `vehicle-history-app`: HTTP `20200` - `vehicle-analytics-app`: HTTP `20300` `vehicle-history-app` 的热历史查询只依赖 Kafka 与 TDengine:RAW 帧的 `parsedJson`、`metadataJson`、`rawUri` 已进入 TDengine `raw_frames`。GB32960/JT808 接入服务可以继续保留 `SINK_ARCHIVE_PATH` 作为原始 `.bin` 冷备,history 本机模板不再要求共享 archive root。 `vehicle-history-app` 的生产高吞吐模板是 TDengine-only: - 不暴露 DuckDB 或文件型明细库启动变量,避免本地文件索引进入实时消费热路径。 - `TDENGINE_TELEMETRY_FIELDS_ENABLED=false`:默认只写 `raw_frames` 和协议位置表,避免逐字段写放大。 - 通用 RAW 查询默认直接返回 TDengine `raw_frames` 中的 `parsedJson`/`parsedFields`;后续解析器新增字段后,优先通过 replay 或专用解析任务补写 TDengine,而不是让 history 服务依赖本地 archive 挂载。 - 如果需要逐字段趋势宽表,可以显式打开 `TDENGINE_TELEMETRY_FIELDS_ENABLED`;开启后要重新压测 Kafka lag 和 TDengine 写入吞吐。 ## 生成 plist 在仓库根目录执行: ```bash export PROJECT_ROOT="$(pwd)" export JAVA_BIN="$(/usr/libexec/java_home 2>/dev/null)/bin/java" export INGEST_ARCHIVE_PATH="$PROJECT_ROOT/data/archive-live" export TDENGINE_JDBC_URL='jdbc:TAOS-WS://:6041/vehicle_ts' export TDENGINE_USERNAME='root' export TDENGINE_PASSWORD='' export VEHICLE_IDENTITY_MYSQL_JDBC_URL='jdbc:mysql://:3306/vehicle_ingest' export VEHICLE_IDENTITY_MYSQL_USERNAME='' export VEHICLE_IDENTITY_MYSQL_PASSWORD='' export VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL='60s' export MYSQL_JDBC_URL="$VEHICLE_IDENTITY_MYSQL_JDBC_URL" export MYSQL_USERNAME="$VEHICLE_IDENTITY_MYSQL_USERNAME" export MYSQL_PASSWORD="$VEHICLE_IDENTITY_MYSQL_PASSWORD" export YUTONG_MQTT_ENABLED='false' export YUTONG_MQTT_URI='' export YUTONG_MQTT_TOPIC='#' export YUTONG_MQTT_USERNAME='' export YUTONG_MQTT_PASSWORD='' mkdir -p "$HOME/Library/LaunchAgents" "$PROJECT_ROOT/data" mkdir -p /tmp/lingniu-gb32960-live /tmp/lingniu-jt808-live /tmp/lingniu-yutong-mqtt-live /tmp/lingniu-vehicle-history-live /tmp/lingniu-vehicle-analytics-live for service in gb32960 jt808 yutong-mqtt vehicle-history vehicle-analytics; do sed \ -e "s#__PROJECT_ROOT__#$PROJECT_ROOT#g" \ -e "s#__JAVA_BIN__#$JAVA_BIN#g" \ -e "s#__SHARED_ARCHIVE_PATH__#$INGEST_ARCHIVE_PATH#g" \ -e "s#__TDENGINE_JDBC_URL__#$TDENGINE_JDBC_URL#g" \ -e "s#__TDENGINE_USERNAME__#$TDENGINE_USERNAME#g" \ -e "s#__TDENGINE_PASSWORD__#$TDENGINE_PASSWORD#g" \ -e "s#__VEHICLE_IDENTITY_MYSQL_JDBC_URL__#$VEHICLE_IDENTITY_MYSQL_JDBC_URL#g" \ -e "s#__VEHICLE_IDENTITY_MYSQL_USERNAME__#$VEHICLE_IDENTITY_MYSQL_USERNAME#g" \ -e "s#__VEHICLE_IDENTITY_MYSQL_PASSWORD__#$VEHICLE_IDENTITY_MYSQL_PASSWORD#g" \ -e "s#__VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL__#$VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL#g" \ -e "s#__MYSQL_JDBC_URL__#$MYSQL_JDBC_URL#g" \ -e "s#__MYSQL_USERNAME__#$MYSQL_USERNAME#g" \ -e "s#__MYSQL_PASSWORD__#$MYSQL_PASSWORD#g" \ -e "s#__YUTONG_MQTT_ENABLED__#$YUTONG_MQTT_ENABLED#g" \ -e "s#__YUTONG_MQTT_URI__#$YUTONG_MQTT_URI#g" \ -e "s#__YUTONG_MQTT_TOPIC__#$YUTONG_MQTT_TOPIC#g" \ -e "s#__YUTONG_MQTT_USERNAME__#$YUTONG_MQTT_USERNAME#g" \ -e "s#__YUTONG_MQTT_PASSWORD__#$YUTONG_MQTT_PASSWORD#g" \ "deploy/local/launchctl/com.lingniu.${service}.plist.template" \ > "$HOME/Library/LaunchAgents/com.lingniu.${service}.plist" done ``` 如果生产配置由 Nacos 下发,可以把模板里的 `NACOS_CONFIG_ENABLED` 改成 `true`,并补充 `NACOS_SERVER_ADDR`、`NACOS_NAMESPACE`、`NACOS_GROUP`、`NACOS_USERNAME`、`NACOS_PASSWORD`。端口、Kafka topic、接入服务冷备 archive 路径、TDengine 连接建议仍保留为启动环境变量,便于 Portainer、launchctl 和临时压测保持一致。 history 消费者会按协议和 raw/event 自动拆分 consumer group。设置 `KAFKA_GROUP_HISTORY=vehicle-history-live` 后,实际 group 为: - `vehicle-history-live-gb32960-event` - `vehicle-history-live-gb32960-raw` - `vehicle-history-live-jt808-event` - `vehicle-history-live-jt808-raw` - `vehicle-history-live-yutong-mqtt-event` - `vehicle-history-live-yutong-mqtt-raw` 本地模板与 ECS 生产口径一致,身份绑定只使用 MySQL。启动前必须提供 `VEHICLE_IDENTITY_MYSQL_*` 连接参数;服务会自动创建 `vehicle_identity_binding` 和 `vehicle_identity_binding_registration` 两张表:前者只保存 `plate`、`vin`,后者保存注册字段;服务按刷新周期用注册表里的车牌关联 VIN,后续 raw/event 会带真实 VIN。 ## 启停 ```bash launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.gb32960.plist" launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.jt808.plist" launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.yutong-mqtt.plist" launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.vehicle-history.plist" launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.vehicle-analytics.plist" launchctl list | rg 'com.lingniu.(gb32960|jt808|yutong-mqtt|vehicle-history|vehicle-analytics)' ``` 替换 jar 前先停止服务,避免 KeepAlive 在 jar 拷贝中途拉起半包: ```bash launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.gb32960.plist" || true launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.jt808.plist" || true launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.yutong-mqtt.plist" || true launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.vehicle-history.plist" || true launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.lingniu.vehicle-analytics.plist" || true ``` ## 验证地址 ```bash curl -sS http://127.0.0.1:20100/actuator/health curl -sS http://127.0.0.1:20100/actuator/health/liveness curl -sS http://127.0.0.1:20100/actuator/health/readiness curl -sS http://127.0.0.1:20400/actuator/health curl -sS http://127.0.0.1:20400/actuator/health/liveness curl -sS http://127.0.0.1:20400/actuator/health/readiness curl -sS http://127.0.0.1:20500/actuator/health curl -sS http://127.0.0.1:20500/actuator/health/liveness curl -sS http://127.0.0.1:20500/actuator/health/readiness curl -sS http://127.0.0.1:20200/actuator/health curl -sS http://127.0.0.1:20200/actuator/health/liveness curl -sS http://127.0.0.1:20200/actuator/health/readiness curl -sS http://127.0.0.1:20300/actuator/health curl -sS http://127.0.0.1:20300/actuator/health/liveness curl -sS http://127.0.0.1:20300/actuator/health/readiness ``` Swagger: - GB32960 ingest: `http://127.0.0.1:20100/swagger-ui/index.html` - JT808 ingest: `http://127.0.0.1:20400/swagger-ui/index.html` - Yutong MQTT ingest: `http://127.0.0.1:20500/swagger-ui/index.html` - History query: `http://127.0.0.1:20200/swagger-ui/index.html` - Analytics metrics: `http://127.0.0.1:20300/swagger-ui/index.html`