# apiServer 微服务模板 基于 [CloudWego Hertz](https://github.com/cloudwego/hertz) 的 Go 微服务脚手架,集成 Nacos 服务注册/发现 + gRPC 客户端 + 访问日志上报(Redis → ES)。 ## 项目结构 ``` ├── apps/ # 业务处理层 (Handler) ├── cmd/ │ ├── main_program/ # 主程序入口 (HTTP 服务) │ └── cli_control/ # CLI 工具入口 (httplog 上报) ├── middleware/ # HTTP 中间件 (Recovery, AccessLog, CORS) ├── models/request_models/ # 请求参数模型 ├── proto/ # Protobuf 生成代码 ├── routes/ # 路由定义 ├── utils/ │ ├── httplog/ # HTTP 访问日志采集 & ES 上报 │ ├── logger/ # 日志工具 (logrus) │ ├── nacos/ # Nacos 服务注册/发现/配置 │ ├── redis_tools/ # Redis 连接 & 通用操作 │ ├── request/ # 请求绑定 & 统一响应 │ └── server_cli/ # gRPC 客户端 ├── start.sh / stop.sh / restart.sh ├── .env.example # 环境变量示例 └── go.mod ``` ## 快速开始 ```bash # 1. 复制配置 cp .env.example .env # 编辑 .env 填入实际配置 # 2. 安装依赖 go mod tidy # 3. 开发运行 go run ./cmd/main_program # HTTP 服务 go run ./cmd/cli_control # httplog 上报 # 4. 构建 go build -ldflags="-s -w" -o server ./cmd/main_program go build -ldflags="-s -w" -o cli ./cmd/cli_control ``` ## 模板使用步骤 1. 拉取该项目仓库 2. 复制 `.env.example` 为 `.env` 并修改配置 3. 全局替换 `apiServer_service` 为你的项目模块名 4. 修改 `go.mod` 中的 module 名称 5. 新建 Gitea 仓库,修改本地仓库地址 --- ## 模块说明 ### cmd/main_program — HTTP 主服务 启动 Hertz HTTP 服务,绑定路由和中间件,可选注册到 Nacos。 ```bash go run ./cmd/main_program ``` 启动流程:加载 `.env` → 校验 `HOST`/`PORT` → 注册中间件(Recovery、AccessLog、CORS)→ 绑定路由 → Nacos 注册(可选)→ 启动 HTTP 监听 → 等待信号优雅关闭。 ### cmd/cli_control — httplog 日志上报 独立后台进程,从 Redis 队列中消费访问日志,批量写入 Elasticsearch。 ```bash go run ./cmd/cli_control ``` 启动后会以轮询方式从 `ES_REDIS_KEY` 队列中批量 pop 日志条目,组装 ES `_bulk` 请求写入 `ES_INDEX_PREFIX-YYYY.MM.DD` 索引。 ### middleware — HTTP 中间件 在 `cmd/main_program/routs.go` 中统一注册: ```go r.Use(middleware.Recovery()) // panic 恢复,防止单个请求崩溃整个服务 r.Use(middleware.AccessLog()) // 请求日志(方法、路径、状态码、耗时) r.Use(middleware.CORS()) // 跨域支持 ``` ### utils/httplog — 访问日志采集 Hertz Tracer 实现,在请求完成后采集完整的访问事件(方法、路径、状态码、耗时、请求体、响应体等),通过 Redis List 异步缓冲。 **在主服务中接入:** ```go import ( "apiServer_service/utils/httplog" "apiServer_service/utils/redis_tools" "github.com/cloudwego/hertz/pkg/app/server" ) rdb := redis_tools.ConnectRedis() tracer := httplog.NewRedisAccessLogTracer(rdb, "access_log", "my-service", httplog.WithSkipPrefix("/health"), // 跳过健康检查路径 httplog.WithMaxResponseBody(4096), // 响应体最大采集 4KB httplog.WithUserIDExtractor(func(c *app.RequestContext) uint { // 根据你的认证方式提取 user_id return 0 }), ) h := server.Default(server.WithTracer(tracer)) ``` **特性:** - 敏感字段自动脱敏(password, token, secret 等) - multipart 文件字段替换为 `[file]` 占位符 - 非文本响应自动跳过(图片、zip 等) - 异步写入 Redis,队列满时丢弃(不阻塞业务) ### utils/redis_tools — Redis 工具 单例连接,提供通用 KV 和 List 操作: ```go import "apiServer_service/utils/redis_tools" // 连接(全局只初始化一次) rdb := redis_tools.ConnectRedis() // KV 操作 redis_tools.SetCache("key", "value", 10*time.Minute) val, err := redis_tools.GetCache("key") redis_tools.Del("key1", "key2") redis_tools.Exists("key") // List 操作 redis_tools.AddToList("queue", "item") items, _ := redis_tools.GetAllFromList("queue") redis_tools.RemoveFromList("queue", "item") ``` ### utils/nacos — Nacos 服务注册/发现/配置 ```go import "apiServer_service/utils/nacos" // 注册当前服务(读取 NACOS_SERVICE_* 环境变量) nacos.RegisterService() // 发现服务(带内存缓存) instance, err := nacos.DiscoverService("user-service") addr := instance.Ip + ":" + strconv.Itoa(int(instance.Port)) // 配置管理 content := nacos.GetConfig("app.yaml", "DEFAULT_GROUP") nacos.AddConfig("app.yaml", "DEFAULT_GROUP", "key: value") ``` ### utils/server_cli — gRPC 客户端 通过 Nacos 服务发现获取 gRPC 地址,连接复用: ```go import "apiServer_service/utils/server_cli" err := server_cli.ReportVisit(token, note, ip, os, point, userId) defer server_cli.CloseGrpcConn() ``` ### utils/request — 请求绑定 & 统一响应 ```go import "apiServer_service/utils/request" // 参数绑定(失败自动返回 400) var req MyRequest if err := request.BindRequestStruct(c, &req); err != nil { return } // 统一响应 request.Success(c, data) // 200 {"code":200,"message":"Success","data":...} request.BadRequest(c, "参数错误") // 400 request.Unauthorized(c, "未登录") // 401 request.NotFound(c, "资源不存在") // 404 request.Error(c, 500, "服务器内部错误") // 自定义状态码 request.FileResponse(c, "/path/to/file", "download.zip") ``` ### routes — 路由定义 在 `routes/` 下按模块拆分路由文件,在 `cmd/main_program/routs.go` 中注册: ```go func SetupRoutes(r *server.Hertz) { r.Use(middleware.Recovery()) r.Use(middleware.AccessLog()) r.Use(middleware.CORS()) api := r.Group("/api") { routes.RegisterIndexRoutes(api) // routes.RegisterUserRoutes(api) // 新增模块在此注册 } } ``` --- ## 生产部署 ### 构建 ```bash go build -ldflags="-s -w" -o server ./cmd/main_program go build -ldflags="-s -w" -o cli ./cmd/cli_control ``` ### 首次安装(systemd 服务注册) 将二进制、`.env`、脚本和 `deploy/` 目录上传到服务器后执行: ```bash chmod +x install.sh start.sh stop.sh restart.sh server cli # 安装 systemd 服务 + 设置开机自启 sudo bash install.sh ``` `install.sh` 会自动: 1. 将 `deploy/*.service` 适配当前路径后复制到 `/etc/systemd/system/` 2. 执行 `systemctl daemon-reload` 3. 执行 `systemctl enable server cli` 开机自启 ### 日常运维 ```bash bash start.sh # 启动全部服务 bash stop.sh # 停止全部服务 bash restart.sh # 重启全部服务 ``` 也可以直接使用 `systemctl` 管理单个服务: ```bash systemctl status server # 查看主服务状态 systemctl status cli # 查看 CLI 状态 systemctl restart server # 只重启主服务 journalctl -u server -f # 查看主服务实时日志 journalctl -u cli -f --since today # 查看 CLI 今日日志 ``` ### systemd 服务特性 | 特性 | 说明 | |------|------| | 开机自启 | `install.sh` 执行后自动启用 | | 崩溃自动重启 | `Restart=always`,server 间隔 3s,cli 间隔 5s | | 优雅关闭 | `KillSignal=SIGTERM`,等待 10s 超时后 SIGKILL | | 环境变量 | 通过 `EnvironmentFile` 加载 `.env` | | 文件描述符 | `LimitNOFILE=65536` | | 日志 | 同时写入 `logs/*.out` 和 `journalctl` | ### 部署目录结构 ``` /root/ ├── server # HTTP 主服务二进制 ├── cli # httplog 上报二进制 ├── .env # 环境配置 ├── deploy/ │ ├── server.service # systemd 服务单元(模板) │ └── cli.service ├── install.sh # 首次安装脚本 ├── start.sh # 启动 ├── stop.sh # 停止 ├── restart.sh # 重启 └── logs/ ├── server.out └── cli.out ``` ## 内置功能清单 - Hertz HTTP 框架 + 路由分组 - Recovery / AccessLog / CORS 中间件 - 统一 JSON 响应格式 - 参数绑定与校验 - HTTP 访问日志采集 → Redis 缓冲 → ES 批量上报 - Redis 工具(单例连接池) - Nacos 服务注册、发现、配置管理 - gRPC 客户端(含连接复用) - 彩色日志输出 + 文件日志 - 优雅关闭 (Graceful Shutdown) - systemd 服务管理(开机自启 + 崩溃自动重启)