iotlcm-sms:独立短信 HTTP/gRPC 服务,支持阿里云与腾讯云。 - Go module 迁移至 git.5iot.com/iotlcm-public/iotlcm-sms - 补 Apache-2.0 LICENSE,重写 README(特性/配置/接口/部署/目录/测试) - 配置改为 config.example.yaml + SMS_* 环境变量注入,仓库不含真实凭证 - 清理内部标识(私有镜像仓库 / 公司名 / 客户签名 / 真实模板ID / 真实手机号) - 统一 LF 行尾并加 .gitattributes
6.1 KiB
iotlcm-sms
独立短信发送服务,HTTP + gRPC 双协议共用同一套实现,支持阿里云与腾讯云短信。
服务自身可持有全局供应商凭证与签名,调用方只需传模板 ID 和短信内容;也可以在每次请求里显式传入完整凭证,由调用方自行管理。
特性
- 双协议:
POST /api/v1/sms/send(HTTP)与PbNotice.Notice/Send(gRPC)共用internal/sms实现 - 双供应商:
serviceType=1阿里云、serviceType=2腾讯云 - 配置三级合并:
config.yaml默认值 →SMS_*环境变量覆盖 → 调用方请求显式字段优先 - 凭证不落库:真实凭证只放在服务器上的
config.yaml(只读挂载),仓库内只保留config.example.yaml - 健康检查:
GET /healthz - gRPC reflection:可直接用
grpcurl查看线上接口
快速开始
go mod tidy
go run .
默认监听 :8080,gRPC 默认监听 :11200:
HTTP_ADDR=:9090 GRPC_ADDR=:11201 go run .
健康检查:
curl http://127.0.0.1:8080/healthz
配置
配置文件
查找顺序:SMS_CONFIG_FILE 指定路径 → ./config.yaml → ./config/config.yaml → /app/config/config.yaml。
# config.yaml(真实凭证只放服务器,不要提交 Git)
serviceType: "1" # 1=阿里云,2=腾讯云
regionId: cn-hangzhou
accessKeyId: ""
accessKeySecret: ""
signName: "示例签名"
# templateId: "" # 模板 ID 由调用方按业务场景传入
# sdkAppId: "" # 仅腾讯云需要
环境变量
| 变量 | 作用 |
|---|---|
SMS_SERVICE_TYPE |
1=阿里云,2=腾讯云 |
SMS_REGION_ID |
区域 ID,默认 cn-hangzhou |
SMS_ACCESS_KEY_ID |
AccessKey ID |
SMS_ACCESS_KEY_SECRET |
AccessKey Secret |
SMS_SDK_APP_ID |
腾讯云 SDKAppID |
SMS_SIGN_NAME |
短信签名 |
SMS_TEMPLATE_ID |
默认模板 ID(通常由调用方按场景传入) |
SMS_CONFIG_FILE |
指定配置文件路径 |
HTTP_ADDR |
HTTP 监听地址,默认 :8080 |
GRPC_ADDR |
gRPC 监听地址,默认 :11200 |
启动日志会打印实际加载的配置来源(file=... env=...),便于排查。
发送短信
阿里云
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
-H 'Content-Type: application/json' \
-d '{
"config": {
"serviceType": "1",
"regionId": "cn-hangzhou",
"accessKeyId": "your-access-key-id",
"accessKeySecret": "your-access-key-secret",
"signName": "your-sign-name",
"templateId": "SMS_000000001"
},
"params": {
"phoneNumber": "138****8000",
"msgContent": "{\"code\":\"123456\"}"
}
}'
腾讯云
curl -X POST http://127.0.0.1:8080/api/v1/sms/send \
-H 'Content-Type: application/json' \
-d '{
"config": {
"serviceType": "2",
"sdkAppId": "1400000000",
"accessKeyId": "your-secret-id",
"accessKeySecret": "your-secret-key",
"signName": "your-sign-name",
"templateId": "000000"
},
"params": {
"phoneNumber": "138****8000",
"msgContent": "123456|5"
}
}'
serviceType=1(阿里云)时msgContent为模板参数 JSONserviceType=2(腾讯云)时多个模板参数用|分隔- 多个手机号用英文逗号分隔;腾讯云号码使用 E.164 格式
成功响应:
{
"code": 0,
"message": "发送成功",
"data": {
"provider": "aliyun",
"requestId": "provider-request-id"
}
}
参数错误返回 HTTP 400,上游短信平台调用失败返回 HTTP 502。响应体中的 code 同步表示业务结果。
gRPC 接口
接口名:PbNotice.Notice/Send。
请求中的 config 和 params 是 JSON 编码的 bytes,字段内容与 HTTP 接口一致;响应结果放在 head.code 和 head.message,routerId、traceId 原样返回。
protobuf 源文件位于 proto/,生成的客户端与服务端代码位于 api/pb/。
grpcurl -plaintext 127.0.0.1:11200 list
grpcurl -plaintext 127.0.0.1:11200 describe PbNotice.Notice
容器化部署
docker build -t iotlcm-sms:latest -f build/Dockerfile .
生产部署推荐用不可变版本标签:
IOTLCM_SMS_IMAGE=<your-registry>/iotlcm-sms:<commit> docker compose -f deploy/docker-compose.yml up -d
deploy/docker-compose.yml 的约定:
- HTTP 端口
11190,gRPC 端口11200 - 真实凭证放在
deploy/config.yaml(不入库),以只读方式挂载到/app/config/config.yaml - 容器
read_only: true,仅/tmp为 tmpfs,启用no-new-privileges
目录结构
iotlcm-sms/
├── main.go # HTTP + gRPC 装配、环境变量注入
├── api/pb/ # 生成的 protobuf 代码
├── proto/ # protobuf 源文件
├── internal/sms/ # Provider 接口 + Registry + 供应商实现
│ ├── aliyun/ # 阿里云实现
│ └── tencent/ # 腾讯云实现
├── internal/httpapi/ # HTTP 路由与处理器
├── internal/grpcapi/ # gRPC 服务实现
├── examples/ # HTTP / gRPC 示例客户端与请求样例
├── tools/ # 辅助工具(如列出阿里云模板)
├── build/Dockerfile # 多阶段构建
└── deploy/ # docker-compose 编排
测试
go test ./...
go test ./... 不访问真实短信平台。
示例客户端:
go run ./examples/http_client
go run ./examples/http_client http://127.0.0.1:9090/api/v1/sms/send
go run ./examples/grpc_client examples/requests/aliyun.local.json
go run ./examples/grpc_client examples/requests/tencent.local.json 127.0.0.1:11200
examples/requests/aliyun.json、tencent.json 使用环境变量占位符,可入库;同目录的 *.local.json 是含本机测试凭证的可直接重放请求,已被 .gitignore 排除。
相关项目
本服务是 iotlcm 物联网卡全生命周期管理平台的一部分,负责向客户与设备侧下发短信通知(到期提醒、告警通知等)。