记录一下go工程实践
系统功能流程
用户下单流程
- 用户浏览商品,商品服务通过 Redis 提供快速的商品信息。
- 用户点击下单,订单服务生成订单并调用支付服务完成支付。
- 支付成功后,订单服务通过消息队列通知商品服务更新库存。
- 推荐服务根据订单数据更新用户的兴趣标签。
社交分享流程
- 用户在抖音商城下单后,可以一键生成短视频分享。
- 社交分享服务提供商品链接,其他用户通过链接访问商城并购买。
Hertz服务
Hertz是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttp 、 gin 、 echo 的优势, 并结合字节跳动内部的需求,使其具有高易用性、高性能、高扩展性等特点,目前在字节跳动内部已广泛使用。
启动Hertz服务
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main() {
h := server.Default()
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
ctx.Data(consts.StatusOK, consts.MIMETextPlain, []byte("hello world"))
})
h.Spin()
}
成功启动了一个 Hertz 服务,并实现了简单的 /hello 路由。
安装 Gorm
运行以下命令安装 Gorm 和 MySQL 驱动:
go get -u gorm.io/driver/mysql
部署服务
目标
- 将服务从本地环境部署到服务器或云平台。
部署步骤
-
打包服务
-
构建可执行文件:
bash go build -o hertz-app main.go
-
-
运行在 Linux
-
将可执行文件和配置文件拷贝到服务器。
-
在服务器上运行:
bash ./hertz-app
-
-
使用容器化部署
-
创建一个
Dockerfile:dockerfile FROM golang:1.20 WORKDIR /app COPY . . RUN go build -o hertz-app main.go CMD ["./hertz-app"] -
构建并运行:
docker build -t hertz-app . docker run -p 8080:8080 hertz-app
-
AI生成文件结构
├── cmd/ # 程序入口 │ ├── main.go # 服务启动主文件 │ ├── migrate.go # 数据库迁移入口 ├── configs/ # 配置文件 │ └── config.yaml # 配置文件(如数据库、Redis、服务配置等) ├── internal/ # 核心业务代码 │ ├── routers/ # 路由管理 │ │ └── router.go # 注册所有路由 │ ├── handlers/ # 控制器层(请求处理) │ │ ├── user_handler.go # 用户模块处理逻辑 │ │ └── product_handler.go # 商品模块处理逻辑 │ ├── services/ # 服务层(业务逻辑) │ │ ├── user_service.go # 用户模块服务逻辑 │ │ └── product_service.go # 商品模块服务逻辑 │ ├── models/ # 数据层(数据库模型和访问逻辑) │ │ ├── user_model.go # 用户数据模型 │ │ └── product_model.go # 商品数据模型 │ ├── middlewares/ # 中间件 │ │ ├── logger_middleware.go # 日志中间件 │ │ └── auth_middleware.go # 认证中间件 │ └── utils/ # 工具类 │ ├── logger.go # 日志工具 │ └── response_util.go # 通用响应工具 ├── pkg/ # 可复用的第三方工具或组件 │ ├── db/ # 数据库连接管理 │ │ └── db.go # 初始化 MySQL 数据库 │ ├── cache/ # 缓存管理 │ │ └── redis.go # 初始化 Redis 缓存 │ ├── consul/ # 服务注册与发现 │ │ └── consul.go # Consul 相关逻辑 │ └── telemetry/ # 性能监控工具 │ └── trace.go # OpenTelemetry 集成 ├── test/ # 测试文件夹 │ ├── handlers_test.go # 处理函数测试 │ ├── services_test.go # 服务逻辑测试 │ └── integration/ # 集成测试 ├── Dockerfile # 容器化配置文件 ├── go.mod # Go 依赖管理文件 ├── go.sum # Go 依赖版本锁定文件 └── README.md # 项目说明文档
具体模块命名和功能划分
1. cmd/
- main.go:程序主入口
2. configs/
- config.yaml:存储全局配置。
3. internal/routers/
- router.go:集中管理路由。
4. internal/handlers/
- user_handler.go:处理用户相关的请求。
5. internal/services/
- user_service.go:用户业务逻辑。
6. internal/models/
- user_model.go:用户数据库模型和操作。
7. internal/middlewares/
- logger_middleware.go:日志中间件。
具体实现
实现 AuthService
在 internal/auth/auth.go 中实现认证服务:
import (
"context"
"fmt"
"sync"
)
// AuthServiceServer 实现 AuthService 的 gRPC 接口
type AuthServiceServer struct {
tokenStore map[string]int32 // 模拟存储 token -> user_id 映射
mu sync.Mutex // 用于并发访问 tokenStore
}
// NewAuthServiceServer 创建一个新的服务实例
func NewAuthServiceServer() *AuthServiceServer {
return &AuthServiceServer{
tokenStore: make(map[string]int32),
}
}
// DeliverTokenByRPC 实现令牌生成逻辑
func (s *AuthServiceServer) DeliverTokenByRPC(ctx context.Context, req *DeliverTokenReq) (*DeliveryResp, error) {
token := fmt.Sprintf("token-%d", req.UserId) // 简单生成 token
s.mu.Lock()
s.tokenStore[token] = req.UserId
s.mu.Unlock()
return &DeliveryResp{Token: token}, nil
}
// VerifyTokenByRPC 实现令牌验证逻辑
func (s *AuthServiceServer) VerifyTokenByRPC(ctx context.Context, req *VerifyTokenReq) (*VerifyResp, error) {
s.mu.Lock()
defer s.mu.Unlock()
_, exists := s.tokenStore[req.Token]
return &VerifyResp{Res: exists}, nil
}
编写 HTTP 接口
通过 HTTP 将认证服务暴露给前端,在 internal/handlers/auth_handler.go 中实现:
import (
"context"
"log"
"net/http"
"project-root/internal/auth"
"github.com/cloudwego/hertz/pkg/app"
)
func DeliverTokenHandler(ctx context.Context, c *app.RequestContext) {
userID := c.Query("user_id")
if userID == "" {
c.JSON(http.StatusBadRequest, map[string]string{"error": "user_id is required"})
return
}
// 调用 gRPC 服务
conn, err := grpc.Dial(":50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to connect to gRPC server: %v", err)
}
defer conn.Close()
client := auth.NewAuthServiceClient(conn)
resp, err := client.DeliverTokenByRPC(ctx, &auth.DeliverTokenReq{UserId: 1}) // 示例 user_id
if err != nil {
c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to deliver token"})
return
}
c.JSON(http.StatusOK, map[string]string{"token": resp.Token})
}
To be continue