关于 go-service
Go Service 是用于构建 Go 微服务的开发技能,面向采用标准库、SQL 和命令行工具的服务开发。内容涵盖依赖注入、HTTP 处理器、结构化日志、数据库代码生成和测试工作流。
主要功能
- 01使用标准库构建服务
- 02组合 HTTP 中间件
- 03显式传递依赖
- 04使用 slog 结构化日志
- 05通过 sqlc 生成类型安全代码
- 06构建可组合命令行界面
适用场景
- 01设计 Go 微服务结构
- 02实现 HTTP 处理器与中间件
- 03为 SQL 模式变更生成代码
- 04构建服务配套的 CLI
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ brojonat/ llmsrules --skill go-service安装后在 Claude Code 中输入/go-service确认已加载。
详细介绍
Go 服务
适用于生产环境的 Go 微服务,使用标准库、sqlc 编写 SQL、urfave/cli 处理命令,并使用 slog 进行结构化日志记录。
核心原则
- 把一件事做好:每个组件只负责一项职责
- 简单优于复杂:使用纯 JSON、标准 SQL 和环境变量
- 组合:围绕管道和标准接口进行设计
- 依赖显式传递:不使用全局变量或单例,通过参数传递所有内容
项目结构
.
├── cmd/
│ ├── server/ # Backend service entry point
│ └── client/ # CLI entry point
├── cli/ # CLI implementation
├── client/ # Public client library
├── internal/ # Internal packages
│ └── db/ # Generated code (from sqlc)
├── migrations/ # SQL schema migrations
├── queries/ # SQL queries for sqlc
├── examples/ # Usage examples
├── testdata/ # Test fixtures
├── Makefile
├── .air.toml # Air configuration
├── sqlc.yaml # sqlc configuration
├── go.mod
├── README.md
└── CHANGELOG.md
处理函数模式
编写返回 http.Handler 的函数(遵循 Mat Ryer 的方法):
func handleListWallets(store TransactionStore, logger *slog.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
wallets, err := store.ListWallets(ctx)
if err != nil {
logger.ErrorContext(ctx, "failed to list wallets", "error", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(wallets)
})
}
中间件组合
type adapter func(http.Handler) http.Handler
// adaptHandler applies adapters in reverse order so the first is outermost
func adaptHandler(h http.Handler, adapters ...adapter) http.Handler {
for i := len(adapters) - 1; i >= 0; i-- {
h = adapters[i](h)
}
return h
}
// Usage:
mux.Handle("GET /wallets", adaptHandler(
handleListWallets(store, logger),
withRequestID(),
withLogging(logger),
withMetrics(promRegistry),
withJWTAuth(jwtSecret),
))
依赖注入
// Bad: hidden dependency on global
var db *sql.DB
func SaveTransaction(txn *Transaction) error {
_, err := db.Exec("INSERT INTO transactions ...")
return err
}
// Good: explicit dependencies
type TransactionStore interface {
Save(ctx context.Context, txn *Transaction) error
}
type PostgresStore struct {
db *sql.DB
}
func NewPostgresStore(db *sql.DB) *PostgresStore {
return &PostgresStore{db: db}
}
// Dependencies are clear at construction time
store := NewPostgresStore(db)
poller := NewWalletPoller(solanaClient, store, natsConn)
使用 slog 进行结构化日志记录
func setupLogger(level slog.Level) *slog.Logger {
opts := &slog.HandlerOptions{Level: level}
return slog.New(slog.NewJSONHandler(os.Stderr, opts))
}
// Most logs at DEBUG level
logger.DebugContext(ctx, "polling wallet", "wallet", walletAddress, "last_slot", lastSlot)
// INFO for lifecycle events only
logger.InfoContext(ctx, "server started", "addr", addr, "version", version)
// ERROR for failures
logger.ErrorContext(ctx, "failed to store transaction", "error", err, "signature", sig)
通过 LOG_LEVEL 环境变量控制日志详细程度。生产环境默认为 WARN,开发环境默认为 DEBUG。
使用 sqlc 编写 SQL
编写 SQL,生成类型安全的 Go 代码:
# sqlc.yaml
version: "2"
sql:
- engine: "postgresql"
queries: "queries/"
schema: "migrations/"
gen:
go:
package: "db"
out: "internal/db"
架构变更后务必重新生成代码,并提交生成的代码。
错误处理
// Always handle errors
txns, err := client.GetTransactions(ctx)
if err != nil {
return fmt.Errorf("failed to get transactions: %w", err)
}
使用 urfave/cli 构建 CLI
构建简洁、可组合的命令行界面,支持自动绑定环境变量并提供内置帮助。
开发工作流
- 规划:先设计接口
- 先写测试(TDD):编写测试,编写最精简的代码,然后重构
- 服务端 + 客户端:每项服务端功能都配套一个客户端方法和一个 CLI 子命令
- Makefile:
make test、make lint、make dev、make build-server - 热重载:开发时使用 Air
- 代码检查:使用严格配置的
golangci-lint,修复所有警告
Unix 哲学
- 默认保持静默:仅在发生错误或重要事件时输出
- 标准输出使用 JSON:输出机器可读内容;为人类用户提供
--format=table|json|csv - 错误输出到标准错误:保持标准输出整洁,便于管道处理
- 退出码:0 = 成功,非零 = 错误
评论
支持 Markdown