[Go框架三件套详解 | 青训营笔记]
这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
一、本课重点内容
- 课程介绍
- 课程目标介绍
- 三件套(Gorm、Kitex、Hertz)介绍
- 三件套的使用
- 基本用法与常用 API 详解
- 实战案例介绍
- 实战项目介绍
- 实战项目功能介绍
- 实战项目关键代码讲解
二、详细知识点介绍
2.1 课程介绍
2.1.1 课程目标介绍
- 将前面几节课的知识运用到项目中
- 掌握 Gorm/Kitex/Hertz 的基本用法
- 通过学习实战案例,完成日常后端开发任务
2.1.2 三件套介绍
- Gorm:ORM 框架
- Kitex:Golang 微服务 RFC 框架
- Hertz:HTTP 框架
2.2 Gorm 的基本使用
Gorm 的约定(默认)
- 使用 ID 的字段作为主键
- 使用结构体的蛇形负数作为表名
- 字段名的蛇形作为列名
- 使用
CreateAtUpdatedAt字段作为创建、更新时间
Gorm 支持的数据库
- MySQL SQLServer PostgreSQL SQLite
- 通过驱动来连接数据库,可以复用/自行开发驱动
Gorm 创建数据
p := &Product{Code: "D42", ID: 1}
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)
使用 clause.OnConflict 处理数据冲突,使用 default 标签为字段定义默认值。
Gorm 查询数据
- 使用
Find查询多条数据,查询不到数据不会返回错误 - 当使用结构体作为条件查询时,GORM 只会查询非零值字段
Gorm 更新数据
使用 struct 更新时,只会更新非零值。如果需要更新零值可以使用 Map 或者 select 选择字段。
Gorm 删除数据
- 物理删除
db.delete(&User{}, 10) - 软删除
gorm.DeleteAt,使用Unscoped可以查询被软删除的数据
Gorm 事务
BeginRollbackCommit用于使用事务Tansaction用于自动提交事务
Gorm Hook
- Gorm 提供了 CURD 的 Hook 能力
2.3 Kitex
安装 Kitex 代码生成工具
$ go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
$ go install github.com/cloudwego/thriftgo@latest
定义 IDL
使用 IDL 定义服务与接口
namespace go api
struct Request {
1: string message
}
struct Response {
1: string message
}
service Echo {
Response echo(1: Request req)
}
Kitex 生成代码
kitex -module example -service example echo.thrift
build.sh构建脚本kitex_genIDL 内容相关的生成代码main.go程序入口handler.go用户在该文件里实现 IDL service 定义的方法
创建 Client
c, err := echo.NewClient("example", client.WithHostPort("0.0.0.0:8888"))
if err != nil{
log.Fatal(err)
}
发起请求
req := &api.Request{Message: "My request"}
resp, err := c.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
if err != nil {
log.Fatal(err)
}
log.Println(resp)
2.4 Hertz
Hertz 的基本使用
使用 Hertz 实现,服务监听 8080端口并注册了一个 GET 方法的路由函数
func main() {
h := server.Default(server.WithHostPosts("0.0.0.0:8080"))
h.GET("/ping",
func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
})
h.Spin()
}
Hertz 路由
Hertz 提供了 GET、POST、PUT、DELETE、ANY 等方法用于注册路由
Hertz 路由
- Hertz 提供了路由组(Group)的能力,用于支持路由分组的功能
- 提供了参数路由和通配路由,路由的优先级为:静态路由 命名路由 通配路由
Hertz 参数绑定
Hertz 提供了 Bind、Validate、BindAndValidate 函数用于进行参数绑定和校验
Hertz 中间件
Hertz 中间件主要分为客户端中间件和服务端中间件。
Hertz Client
Hertz 提供了 Hertz Client 用于帮助用户发送 HTTP 请求
Hertz 代码生成工具
Hertz 提供了代码生成工具 Hz,通过定义 IDL 文件即可生成对应的基础服务代码
三、实践练习例子
3.1 项目介绍
笔记项目是由 Hertz、Kitex、Gorm 搭建出来的具有一定业务逻辑的后端 API 项目。
3.2 Hertz 关键代码
func CreateNote(ctx context.Context, c *app.RequestContext) {
var noteVar NoteParam
if err := c.Bind(¬eVar); err != nil {
SendResponse(c, errno.ConvertErr(err), nil)
return
}
if len(noteVar.Title) == 0 || len(noteVar.Content) == 0 {
SendResponse(c, errno.ParamErr, nil)
return
}
claims := jwt.ExtractClaims(ctx, c)
userID := int64(claims[constants.IdentityKey].(float64))
err := rpc.CreateNote(context.Background(), ¬edemo.CreateNoteRequest{
UserId: userID,
Content: noteVar.Content, Title: noteVar.Title,
})
if err != nil {
SendResponse(c, errno.ConvertErr(err), nil)
return
}
SendResponse(c, errno.Success, nil)
}
3.3 Kitex Client 关键代码
func initNoteRpc() {
r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress})
if err != nil {
panic(err)
}
c, err := noteservice.NewClient(
constants.NoteServiceName,
client.WithMiddleware(middleware.CommonMiddleware),
client.WithInstanceMW(middleware.ClientMiddleware),
client.WithMuxConnection(1), // mux
client.WithRPCTimeout(3*time.Second), // rpc timeout
client.WithConnectTimeout(50*time.Millisecond), // conn timeout
client.WithFailureRetry(retry.NewFailurePolicy()), // retry
client.WithSuite(trace.NewDefaultClientSuite()), // tracer
client.WithResolver(r), // resolver
)
if err != nil {
panic(err)
}
noteClient = c
}
// CreateNote create note info
func CreateNote(ctx context.Context, req *notedemo.CreateNoteRequest) error {
resp, err := noteClient.CreateNote(ctx, req)
if err != nil {
return err
}
if resp.BaseResp.StatusCode != 0 {
return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage)
}
return nil
}
3.4 Kitex Server 关键代码
type CreateNoteService struct {
ctx context.Context
}
// NewCreateNoteService new CreateNoteService
func NewCreateNoteService(ctx context.Context) *CreateNoteService {
return &CreateNoteService{ctx: ctx}
}
// CreateNote create note info
func (s *CreateNoteService) CreateNote(req *notedemo.CreateNoteRequest) error {
noteModel := &db.Note{
UserID: req.UserId,
Title: req.Title,
Content: req.Content,
}
return db.CreateNote(s.ctx, []*db.Note{noteModel})
}
3.5 Gorm 关键代码
type Note struct {
gorm.Model
UserID int64 `json:"user_id"`
Title string `json:"title"`
Content string `json:"content"`
}
func (n *Note) TableName() string {
return constants.NoteTableName
}
// CreateNote create note info
func CreateNote(ctx context.Context, notes []*Note) error {
if err := DB.WithContext(ctx).Create(notes).Error; err != nil {
return err
}
return nil
}
// MGetNotes multiple get list of note info
func MGetNotes(ctx context.Context, noteIDs []int64) ([]*Note, error) {
var res []*Note
if len(noteIDs) == 0 {
return res, nil
}
if err := DB.WithContext(ctx).Where("id in ?", noteIDs).Find(&res).Error; err != nil {
return res, err
}
return res, nil
}
// UpdateNote update note info
func UpdateNote(ctx context.Context, noteID, userID int64, title, content *string) error {
params := map[string]interface{}{}
if title != nil {
params["title"] = *title
}
if content != nil {
params["content"] = *content
}
return DB.WithContext(ctx).Model(&Note{}).Where("id = ? and user_id = ?", noteID, userID).
Updates(params).Error
}
// DeleteNote delete note info
func DeleteNote(ctx context.Context, noteID, userID int64) error {
return DB.WithContext(ctx).Where("id = ? and user_id = ? ", noteID, userID).Delete(&Note{}).Error
}
四、课后个人总结
本课重点学习了 Go 语言三件套的基本用法。