chore: 初始开源发布
iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。 - Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件) - 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、 库名 xiujie_admin → iotlcm、哨兵名占位 - 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异, 不破坏「校验 MSISDN 格式」等测试断言) - 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像 digest 记录 - Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23), 移除写死的 git 凭据 - README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 已知:3 个测试包在源仓同一提交上同样失败(client/upstream、 service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
FROM golang:1.25-alpine AS builder
|
||||
|
||||
LABEL stage=gobuilder
|
||||
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG BuildVersion
|
||||
ARG BuildTime
|
||||
ARG CommitID
|
||||
|
||||
RUN go build -v -o /build/iotlcm-worker-svr -ldflags "-X main.BuildVersion=${BuildVersion} -X main.CommitID=${CommitID} -X main.BuildTime=${BuildTime}"
|
||||
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /build/iotlcm-worker-svr /app/iotlcm-worker-svr
|
||||
COPY --from=builder /build/build/build_config.yaml /app/config/config.yaml
|
||||
|
||||
CMD ["./iotlcm-worker-svr"]
|
||||
@@ -0,0 +1,346 @@
|
||||
# config.yaml 配置文件
|
||||
# @Link https://github.com/xiujiecn/xiu-admin
|
||||
# @Copyright Copyright (c) 2025 LiXiujie
|
||||
# @Author Lxj <li@xiujie.cn>
|
||||
# @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||
|
||||
# 系统配置
|
||||
system:
|
||||
name: "IotLCM"
|
||||
version: "1.0"
|
||||
description: "IotLCM Server"
|
||||
gfmode: "dev"
|
||||
upload:
|
||||
local:
|
||||
path: "resource/upload"
|
||||
|
||||
# 日志配置
|
||||
logger:
|
||||
path: "/app/logs" # 日志文件路径。默认为空,表示关闭,仅输出到终端
|
||||
file: "{Y-m-d-H}.log" # 日志文件格式。默认为"{Y-m-d}.log"
|
||||
prefix: "" # 日志内容输出前缀。默认为空
|
||||
level: "dev" # 日志输出级别
|
||||
timeFormat: "2006-01-02T15:04:05" # 自定义日志输出的时间格式,使用Golang标准的时间格式配置
|
||||
ctxKeys: [] # 自定义Context上下文变量名称,自动打印Context的变量到日志中。默认为空
|
||||
header: true # 是否打印日志的头信息。默认true
|
||||
stdout: true # 日志是否同时输出到终端。默认true
|
||||
rotateSize: 0 # 按照日志文件大小对文件进行滚动切分。默认为0,表示关闭滚动切分特性
|
||||
rotateExpire: 0 # 按照日志文件时间间隔对文件滚动切分。默认为0,表示关闭滚动切分特性
|
||||
rotateBackupLimit: 0 # 按照切分的文件数量清理切分文件,当滚动切分特性开启时有效。默认为0,表示不备份,切分则删除
|
||||
rotateBackupExpire: 0 # 按照切分的文件有效期清理切分文件,当滚动切分特性开启时有效。默认为0,表示不备份,切分则删除
|
||||
rotateBackupCompress: 0 # 滚动切分文件的压缩比(0-9)。默认为0,表示不压缩
|
||||
rotateCheckInterval: "1h" # 滚动切分的时间检测间隔,一般不需要设置。默认为1小时
|
||||
stdoutColorDisabled: false # 关闭终端的颜色打印。默认开启
|
||||
writerColorEnable: false # 日志文件是否带上颜色。默认false,表示不带颜色
|
||||
|
||||
# 数据库配置
|
||||
database:
|
||||
logger:
|
||||
path: "/app/logs/sql"
|
||||
level: "all"
|
||||
stdout: true
|
||||
default: # 分组名称,可自定义,默认为default
|
||||
link: "mysql:${DB_USER}:${DB_PASSWORD}@tcp(${DB_HOST}:3306)/db_iotlcm?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
|
||||
debug: true #开启调试模式
|
||||
charset: "utf8mb4" # (可选)数据库编码(如: utf8mb4/utf8/gbk/gb2312),一般设置为utf8mb4。默认为utf8。
|
||||
dryRun: false #ORM空跑(只读不写)
|
||||
maxIdle: 30 #连接池最大闲置的连接数
|
||||
maxOpen: 100 #连接池最大打开的连接数
|
||||
maxLifetime: 120 #(单位秒)连接对象可重复使用的时间长度
|
||||
memorydb:
|
||||
# link: "sqlite::@file(jieiotmem.db)?journal_mode=WAL"
|
||||
link: "sqlite::@file(:memory:)"
|
||||
# link: ""
|
||||
debug: true
|
||||
maxIdle: 1
|
||||
maxOpen: 1
|
||||
maxLifetime: 30
|
||||
|
||||
# 缓存配置
|
||||
cache:
|
||||
adapter: "redis" # 缓存适配器,支持:file|redis
|
||||
fileDir: "resource/cache" # 缓存文件目录
|
||||
|
||||
# Redis 配置
|
||||
redis:
|
||||
# 单实例配置
|
||||
default:
|
||||
mode: "single" #redis模式,支持:single|cluster|sentinel
|
||||
sentinelMasterName: "iotlcm-master" #哨兵模式主节点名称
|
||||
address: 192.0.2.29:6379
|
||||
db: 0
|
||||
pass: "${REDIS_PASSWORD}"
|
||||
minIdle: 5 #最小空闲连接数
|
||||
maxIdle: 30 #允许闲置的最大连接数
|
||||
maxActive: 5000 #最大活跃连接数
|
||||
idleTimeout: "60s" #连接最大空闲时间,使用时间字符串例如30s/2m/2d
|
||||
ClientName: "SystemCache"
|
||||
|
||||
|
||||
alipay:
|
||||
appId: "${MOBILE_APP_ID}"
|
||||
privateKey: "MIIEowIBAAKCAQEAg+4BpLO2Fw5GMbrihnugyQqiLBp2RhSbSsvjNyzaG10u900N1DcVlh8HNdcus5AGjfbiUwMQwv7Os+NnFrEZA3CtWLXH+uczHIpcMqJJhoVgA//4gf2TX9K4OSQ1pgJeAhTS9WWMEB7DQLb7A6xGjvBdGNbPAzucGhUmtG1tgZSVOcpWqN2h4dQxkZhfR+6m3AqWAPahpQXsNINHewetQ4mIG+b3+z/OknEll0RDRmw7OihOJ24B51Phy7lewl160LgLpLffyaQUUXcFdcNo+HvWqtIT0mH8WWHAgqfN/WVkMz/WVjE0rzbIVgmpQc7+hMbrcafTmeq0wdnVXrKS6QIDAQABAoIBAEagy1ZuMyRiqueCojTWyHv4altsCzd3Xq+LOWfXZihSGYuhP68FklPc5pJHUaQljazAp+dC891AZk5dhlnak4aI8vRT1OyYi39S/wrIYM5YZs+dYF/O7e/VsnQ800+9YrCAj3N6WqJiparmirkrqFZrjZt3nDmh1u8Ltb3P+1jr90MBEdS+BXaEQP/KGxFiKZyoWFn9B4l0aPFkPExF+hgtRpP3gxFcyljQKGVmSUIkT6V08F1oRm72NQOhcizCh9lX+5HYLTkUVQVg6FHwdZs/KeJcZu6Y829pNSNeJmPbR3VPUpTbvwb0qeEoc42T3H/C0xXUeTYizUpHmhJJqnUCgYEA3Y7Xz+MUk98zRJnlr2BDUc+Qnhv3wmSA5LCcRbxTNNBp4TXjnZknY6Ms9D+nJupL4nw6QSYkO6O+tAEmU6gaSDEHKI1uKqU0zOdbGIHNkMqka3rinQcMJNNRZ8T1FLCPFna7S1FNlxulZgWxjqxP/KvWOEpCWLCLzwZNk8fEd+MCgYEAmHBNbaMmT4WvLevY/OMv/5e5csnDFIcsNJKvFLZZMBLuKT/26YN2ryVUivY+TTyHL5zk4VybeauTdQHIvX6sNJoh2Ii6vthKfSufL4rkMob8PHiG33iHBx3VxAwiVXfu3JHVnbCPcXTOzsdRzT17X3rODK9t8319qfVjd0Gxi8MCgYEAqgNc0+ea+MXD35aAzA0AW5z9inZHruHUjqh/onubFeH//ZOOsHa/5RNoAZ1+tLwBszBEu417LFDAm/xt3rAWSuNkbNfqe8r+M2YtXybsCXmWpetMTdcEmJWX2BspPdEmW8SMgIVJFvVd/GXOFH9hebCa1+MT+l6vShO6H/ahaqUCgYAzon6nr4bPkvHWtTfH2VCt2//mIO+iaXMQz8rcUjP9s7vWXyNMz8oI47UyaNyLnwAPoa5po8vKN9L35EXR4jGEk9z5Er9m267HnVoLaZmIf+66sPzGf3YFdBavlIiguMJBFmjsRB0iijVvF1bfFX9PxTI8W8P7oTtKsaMihy8TrwKBgCgjA8DL8HJbRwZ+V/KWOh8rzO5cBUUH3ThIj6u7Wn4tMlscfi+KFKTXghjSORfIVx8v9v2aNZKwGZ547AOPRXhDF09cLAcCfl7uOrwsftziIeBQnrqtpIqcVTgRwGMIMBJEtQEks5N/E/d+dSL1YQKC76mVC8/RMf52WYKH1QCa"
|
||||
alipayPublicKey: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8Df1EPFkA2xy8NgF0UdRYxsshslUi2XSGTG01oQKsrYNB+rhAw9MJdMlunVQxSrBZsw5N7htXGdiTceaLo/n8Wuibp0cRHdErJSp9GhP4pW2IR3FoCCPYlJlyiCUR/NInI3XMai0UdjgiPX03bvYLRAKE8lLiOSgxNagQmOFLmQYRYfFhGbxDe98XMg83YdSWvZKrlM+vzgk8/aPgq7BFLpbRKqLvd8OXXBHDCaWMSSGwCN0whhdEiwbIhSVgwTgkhWXUuNDmT741/+7D46djp8wX/84SDts0fWb63/in9ATTGtNivJX7HPDyqrmcN2iUi5xFHS9XwYBdT4yu/xFQwIDAQAB"
|
||||
notifyUrl: "https://evolution-starless-municipal.ngrok-free.dev/api/v1/pay/alipay/notify"
|
||||
returnUrl: "http://localhost:5666/enterpriseManagement/tBalanceDetail/tAgentBalance"
|
||||
isProduction: false
|
||||
|
||||
# RabbitMQ 配置(worker 核心依赖)
|
||||
rabbitmq:
|
||||
enabled: true
|
||||
# 示例:amqp://guest:guest@127.0.0.1:5672/
|
||||
# dsn: "amqp://admin:admin@127.0.0.1:5672/"
|
||||
dsn: "amqp://root:sdyz2026@192.0.2.29:5672/"
|
||||
balanceRecharge:
|
||||
retryDelay: "1m" # Alipay 查询失败后的延时重试时间
|
||||
maxRetry: 100 # 最大重试次数,超过后投递到 iotlcm.balance.recharge.dlq
|
||||
batchExecute:
|
||||
executionTimeout: "0s"
|
||||
finalizationTimeout: "10s"
|
||||
retryDelay: "30s"
|
||||
maxRetries: 3
|
||||
|
||||
batch:
|
||||
inbound:
|
||||
upstreamBatchSize: 1
|
||||
upstreamConcurrency: 8
|
||||
upstreamRateLimit:
|
||||
enabled: true
|
||||
mode: "redis"
|
||||
redisGroup: "default"
|
||||
keyPrefix: "iotlcm:worker:inbound:upstream_basic_info"
|
||||
redisFailureFallback: "error"
|
||||
default:
|
||||
qps: 20
|
||||
burst: 1
|
||||
requestCost: 1
|
||||
channels: {}
|
||||
|
||||
# 本月流量全量同步。开发环境常驻监听,Admin 未配置定时任务时不会自动启动全量扫描。
|
||||
trafficUsageFullSync:
|
||||
enabled: true
|
||||
redisKeyPrefix: "flow:"
|
||||
retry:
|
||||
enabled: true
|
||||
maxRounds: 5
|
||||
failureRetention: "720h"
|
||||
producer:
|
||||
# channel requires idx_card_chan_key; never creates or rebuilds an index.
|
||||
scanConcurrency: 4 # SQL burst protection, not a measured optimum
|
||||
pageSize: 100
|
||||
globalHighWaterMark: 10000
|
||||
channelHighWaterMark: 2000
|
||||
pollInterval: "2s"
|
||||
runLockKey: "flow:producer:active"
|
||||
runLockTTL: "5m"
|
||||
consumer:
|
||||
enabled: true
|
||||
chanClientName: "192.0.2.29:8110"
|
||||
chanClientTimeout: "90s"
|
||||
lockKey: "flow:consumer:active"
|
||||
lockTTL: "1m"
|
||||
pollInterval: "1s"
|
||||
leaseTTL: "3m"
|
||||
taskTimeout: "2m"
|
||||
retryDelay: "30s"
|
||||
maxRetryDelay: "30m"
|
||||
claimBatch: 1
|
||||
mobileConcurrency: 30
|
||||
telecomConcurrency: 20
|
||||
genericConcurrency: 1
|
||||
maxChannels: 8
|
||||
channelConcurrency: 1
|
||||
maxInFlight: 8
|
||||
maxAttempts: 5
|
||||
# 失败池告警阀值(条目数):单条 FailureRecord 约 1KB 且保留整个
|
||||
# failureRetention,超阀值只打 WARN 日志,绝不截断 Run。
|
||||
failurePoolAlertThreshold: 50000
|
||||
|
||||
# 独立的日流量同步链路,与月流量/状态同步同构。
|
||||
# 发布配置默认仍关闭:先用手动 API + 单通道 scope 预演,确认产出与旧链路逐字段一致后再打开。
|
||||
trafficUsageDailySync:
|
||||
enabled: false
|
||||
redisKeyPrefix: "daily:"
|
||||
retry:
|
||||
enabled: true
|
||||
maxRounds: 3
|
||||
failureRetention: "720h"
|
||||
producer:
|
||||
pageSize: 100
|
||||
globalHighWaterMark: 0
|
||||
channelHighWaterMark: 0
|
||||
pollInterval: "2s"
|
||||
runLockKey: "daily:producer:active"
|
||||
runLockTTL: "5m"
|
||||
consumer:
|
||||
enabled: false
|
||||
lockKey: "daily:consumer:active"
|
||||
lockTTL: "1m"
|
||||
pollInterval: "1s"
|
||||
leaseTTL: "3m"
|
||||
taskTimeout: "2m"
|
||||
claimBatch: 100
|
||||
mobileConcurrency: 30
|
||||
telecomConcurrency: 8
|
||||
genericConcurrency: 1
|
||||
genericQPS: 1.5
|
||||
templateQPS:
|
||||
sim_yd_v5: 30
|
||||
sim_yd_mock_v5: 30
|
||||
sim_onelink_tengyu_v1: 30
|
||||
sim_onelink_beixin_v1: 30
|
||||
sim_onelink_xuzhou_mobile_v1: 30
|
||||
sim_onelink_lelian_v1: 30
|
||||
sim_onelink_qianxun_v1: 30
|
||||
sim_onelink_henan_xinjing_v1: 30
|
||||
sim_onelink_daihe_v1: 30
|
||||
sim_dx_v1: 25
|
||||
|
||||
# 独立的月流量历史同步链路(写 TDengine 月表),与日流量/状态同步同构。
|
||||
# 注意:这与 trafficUsageFullSync 不是一回事——后者填的是 MySQL 当月快照。
|
||||
# 默认关闭:日流量验证通过后再单独打开。
|
||||
trafficUsageMonthlyHistorySync:
|
||||
enabled: false
|
||||
redisKeyPrefix: "monthly-history:"
|
||||
retry:
|
||||
enabled: true
|
||||
maxRounds: 3
|
||||
failureRetention: "720h"
|
||||
producer:
|
||||
pageSize: 100
|
||||
globalHighWaterMark: 0
|
||||
channelHighWaterMark: 0
|
||||
pollInterval: "2s"
|
||||
runLockKey: "monthly-history:producer:active"
|
||||
runLockTTL: "5m"
|
||||
consumer:
|
||||
enabled: false
|
||||
lockKey: "monthly-history:consumer:active"
|
||||
lockTTL: "1m"
|
||||
pollInterval: "1s"
|
||||
leaseTTL: "3m"
|
||||
taskTimeout: "2m"
|
||||
claimBatch: 100
|
||||
mobileConcurrency: 30
|
||||
telecomConcurrency: 8
|
||||
genericConcurrency: 1
|
||||
genericQPS: 1.5
|
||||
# 三条链路共用出口 IP,各自独立限速,避免互相挤占运营商配额。
|
||||
templateQPS:
|
||||
sim_yd_v5: 30
|
||||
sim_yd_mock_v5: 30
|
||||
sim_onelink_tengyu_v1: 30
|
||||
sim_onelink_beixin_v1: 30
|
||||
sim_onelink_xuzhou_mobile_v1: 30
|
||||
sim_onelink_lelian_v1: 30
|
||||
sim_onelink_qianxun_v1: 30
|
||||
sim_onelink_henan_xinjing_v1: 30
|
||||
sim_onelink_daihe_v1: 30
|
||||
sim_dx_v1: 25
|
||||
|
||||
# 独立的状态同步队列链路,默认关闭以保留旧逻辑作为切换期兜底。
|
||||
simStatusQueueSync:
|
||||
enabled: true
|
||||
redisKeyPrefix: "sim-status:"
|
||||
retry:
|
||||
enabled: true
|
||||
maxRounds: 3
|
||||
failureRetention: "720h"
|
||||
producer:
|
||||
pageSize: 1000
|
||||
globalHighWaterMark: 0
|
||||
pollInterval: "1s"
|
||||
runLockKey: "sim-status:producer:active"
|
||||
runLockTTL: "5m"
|
||||
consumer:
|
||||
enabled: true
|
||||
lockKey: "sim-status:consumer:active"
|
||||
lockTTL: "1m"
|
||||
pollInterval: "1s"
|
||||
leaseTTL: "3m"
|
||||
taskTimeout: "2m"
|
||||
claimBatch: 100
|
||||
mobileConcurrency: 100
|
||||
telecomConcurrency: 8
|
||||
genericConcurrency: 1
|
||||
genericQPS: 1.5
|
||||
networkRetries: 2
|
||||
# 失败池告警阀值(条目数):单条 FailureRecord 约 1KB 且保留整个
|
||||
# failureRetention,超阀值只打 WARN 日志,绝不截断 Run。
|
||||
failurePoolAlertThreshold: 50000
|
||||
templateQPS:
|
||||
sim_yd_v5: 100
|
||||
sim_yd_mock_v5: 100
|
||||
sim_onelink_tengyu_v1: 100
|
||||
sim_onelink_xuzhou_mobile_v1: 100
|
||||
sim_onelink_lelian_v1: 100
|
||||
sim_dx_v1: 5
|
||||
|
||||
# 独立的 IMEI 全量同步链路。
|
||||
# 默认关闭:先完成预演验证(含 lanshang_8000/8003 是否支持 IMEI 的实测)
|
||||
# 再按环境开启,否则未验证的通道会直接进失败池。
|
||||
imeiFullSync:
|
||||
enabled: false
|
||||
redisKeyPrefix: "imei:"
|
||||
retry:
|
||||
enabled: true
|
||||
maxRounds: 5
|
||||
failureRetention: "720h"
|
||||
producer:
|
||||
pageSize: 1000
|
||||
globalHighWaterMark: 0
|
||||
pollInterval: "1s"
|
||||
runLockKey: "imei:producer:active"
|
||||
runLockTTL: "5m"
|
||||
consumer:
|
||||
enabled: false
|
||||
chanClientName: "iotlcm.example.com:8110"
|
||||
chanClientTimeout: "30s"
|
||||
lockKey: "imei:consumer:active"
|
||||
lockTTL: "1m"
|
||||
pollInterval: "1s"
|
||||
leaseTTL: "3m"
|
||||
taskTimeout: "2m"
|
||||
claimBatch: 100
|
||||
mobileConcurrency: 100
|
||||
telecomConcurrency: 8
|
||||
genericConcurrency: 1
|
||||
genericQPS: 1.5
|
||||
networkRetries: 2
|
||||
# 失败池告警阀值(条目数):单条 FailureRecord 约 1KB 且保留整个
|
||||
# failureRetention,超阀值只打 WARN 日志,绝不截断 Run。
|
||||
failurePoolAlertThreshold: 50000
|
||||
templateQPS:
|
||||
sim_yd_v5: 100
|
||||
sim_yd_mock_v5: 100
|
||||
sim_onelink_tengyu_v1: 100
|
||||
sim_onelink_xuzhou_mobile_v1: 100
|
||||
sim_onelink_lelian_v1: 100
|
||||
sim_dx_v1: 5
|
||||
|
||||
# 诊断消费者开关(仅本地调试开)
|
||||
workerDiagnostic:
|
||||
enabled: true
|
||||
|
||||
# 时序数据库配置
|
||||
tsdb:
|
||||
database: "TdEngine" #可选择 TdEngine、Influxdb
|
||||
tdengine:
|
||||
# type: "taosRestful" #http连接方式,端口是6041
|
||||
# dsn: "root:taosdata@http(127.0.0.1:6041)/"
|
||||
type: "taosWS" #websocket连接方式,端口是6041
|
||||
# dsn: "root:taosdata@ws(127.0.0.1:6041)/"
|
||||
dsn: "root:sdyz2026__@ws(192.0.2.29:6041)/"
|
||||
dbName: "iotlcm"
|
||||
maxOpenConns: 100
|
||||
maxIdleConns: 10
|
||||
|
||||
# 上游服务调用
|
||||
upstream:
|
||||
proxy:
|
||||
address: "iotlcm.example.com:8510"
|
||||
# address: "localhost:8510"
|
||||
timeout: 10s
|
||||
gateway:
|
||||
address: "iotlcm.example.com:8110"
|
||||
timeout: 10s
|
||||
Reference in New Issue
Block a user