2. Go 微服务项目初始化:使用 Go Module 创建项目、目录结构设计、基础 HTTP 服务框架搭建
本章是微服务压测闭环工程实践的起点。我们将从零开始,使用 Go Module 初始化项目,设计符合微服务治理规范的目录结构,并搭建一个可运行的基础 HTTP 服务框架。这个框架将作为后续所有压测、监控与部署实验的载体。
2.1 使用 Go Module 初始化项目
Go Module 是 Go 1.11 引入的官方依赖管理方案,也是现代 Go 项目的标准配置。我们使用 go mod init 命令创建模块,模块路径通常采用公司域名+项目名的形式,例如 github.com/company/project。
初始化命令:
mkdir go-microservice-demo
cd go-microservice-demo
go mod init github.com/example/go-microservice-demo
执行后,项目根目录下会生成 go.mod 文件,内容如下:
module github.com/example/go-microservice-demo
go 1.22.0
go.mod 文件记录了模块路径、Go 版本以及依赖项。后续每添加一个外部依赖,Go 都会自动更新此文件并生成 go.sum 用于校验。
2.2 微服务项目目录结构设计
合理的目录结构是微服务项目可维护性的基石。我们采用业界广泛使用的 分层架构 + 领域驱动设计(DDD) 风格,将代码按职责划分为清晰的层级。
| 目录/文件 | 职责 | 说明 |
|---|---|---|
cmd/ |
应用入口 | 每个微服务对应一个子目录,如 cmd/server/main.go |
internal/ |
内部业务逻辑 | 禁止外部包导入,包含 handler、service、repository 等 |
pkg/ |
可复用的公共库 | 如 logger、config、middleware 等 |
api/ |
API 定义 | 存放 protobuf、OpenAPI 规范文件 |
config/ |
配置文件 | YAML/JSON 配置文件,按环境拆分 |
deploy/ |
部署相关 | Dockerfile、K8s 清单、CI/CD 脚本 |
test/ |
集成测试与压测脚本 | 包含 benchmark、loadtest 等 |
Makefile |
项目构建与任务编排 | 统一管理 build、test、lint 等命令 |
最佳实践:
internal 目录是 Go 编译器强制的包隔离机制。放在 internal 下的包只能被其父目录及兄弟目录导入,有效防止外部项目滥用内部 API。
以下是我们为本课程设计的完整目录结构:
go-microservice-demo/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── handler/
│ │ └── user.go
│ ├── service/
│ │ └── user.go
│ ├── repository/
│ │ └── user.go
│ └── model/
│ └── user.go
├── pkg/
│ ├── config/
│ │ └── config.go
│ ├── logger/
│ │ └── logger.go
│ └── middleware/
│ └── recovery.go
├── api/
│ └── proto/
│ └── user.proto
├── config/
│ ├── default.yaml
│ └── production.yaml
├── deploy/
│ ├── Dockerfile
│ └── k8s/
│ └── deployment.yaml
├── test/
│ ├── integration/
│ └── loadtest/
├── go.mod
├── go.sum
└── Makefile
2.3 基础 HTTP 服务框架搭建
我们选择 Go 标准库 net/http 作为基础 HTTP 框架,不引入第三方路由库,以保持最小依赖和最高透明度。后续章节会逐步引入 gin、grpc 等框架进行对比压测。
首先在 cmd/server/main.go 中编写入口代码:
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/example/go-microservice-demo/internal/handler"
"github.com/example/go-microservice-demo/pkg/config"
"github.com/example/go-microservice-demo/pkg/logger"
)
func main() {
// 1. 加载配置
cfg := config.Load()
// 2. 初始化日志
log := logger.New(cfg.LogLevel)
// 3. 注册路由
mux := http.NewServeMux()
mux.HandleFunc("GET /api/v1/health", handler.HealthCheck)
mux.HandleFunc("GET /api/v1/users/{id}", handler.GetUser)
mux.HandleFunc("POST /api/v1/users", handler.CreateUser)
// 4. 创建 HTTP 服务
srv := &http.Server{
Addr: ":" + cfg.Port,
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}
// 5. 优雅启停
go func() {
log.Infof("Server starting on port %s", cfg.Port)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Info("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Info("Server exited")
}
注意: Go 1.22 开始支持
mux.HandleFunc("GET /api/v1/users/{id}", handler) 这种带路径参数的模式匹配。如果你使用 Go 1.21 或更早版本,需要手动解析路径或使用第三方路由库。
接下来实现一个简单的 handler,位于 internal/handler/user.go:
package handler
import (
"encoding/json"
"net/http"
)
type UserResponse struct {
ID string `json:"id"`
Name string `json:"name"`
}
func HealthCheck(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func GetUser(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
resp := UserResponse{ID: id, Name: "User_" + id}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func CreateUser(w http.ResponseWriter, r *http.Request) {
var req struct {
Name string `json:"name"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
resp := UserResponse{ID: "new-001", Name: req.Name}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(resp)
}
2.4 项目初始化流程总览
下图展示了从创建项目到启动 HTTP 服务的完整流程,以及各组件之间的依赖关系:
2.5 验证服务可运行
完成上述代码后,在项目根目录执行以下命令启动服务:
go run ./cmd/server/main.go
服务启动后,使用 curl 验证接口:
# 健康检查
curl http://localhost:8080/api/v1/health
# 输出: {"status":"ok"}
# 获取用户
curl http://localhost:8080/api/v1/users/42
# 输出: {"id":"42","name":"User_42"}
# 创建用户
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}'
# 输出: {"id":"new-001","name":"Alice"}
压测准备: 这个基础服务虽然简单,但已经包含了 HTTP 路由、JSON 序列化、请求解析等核心路径。后续章节将直接使用此服务作为压测目标,对比不同框架、不同配置下的性能差异。
2.6 本章小结
我们完成了以下关键工作:
- 使用
go mod init创建了模块化项目 - 设计了符合微服务治理规范的分层目录结构
- 基于标准库
net/http搭建了可运行的基础 HTTP 服务 - 实现了健康检查、用户查询与创建三个核心接口
- 配置了优雅启停与超时控制,为生产级部署打下基础