Go 框架三件套详解(Web/RPC/ORM) | 青训营笔记

107 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天。学习的三个框架为Gorm(ORM)、Kitex(RPC)、Hertz(http)。使用框架是Web开发的必要技能。

Gorm

Gorm提供与数据库交互的库。基本使用方法如下所示:

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // Migrate the schema
  db.AutoMigrate(&Product{})

  // Create
  db.Create(&Product{Code: "D42", Price: 100})

  // Read
  var product Product
  db.First(&product, 1) // find product with integer primary key
  db.First(&product, "code = ?", "D42") // find product with code D42

  // Update - update product's price to 200
  db.Model(&product).Update("Price", 200)
  // Update - update multiple fields
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

  // Delete - delete product
  db.Delete(&product, 1)
}

在规定中,Gorm的数据库名称默认为类型的蛇形命名,值的名称默认为对应的蛇形命名。要修改表名,可以添加tableName函数。

使用注解为类型提供默认值,数据库类型等信息:

type User struct {
  ID         int64
  Name       string `gorm:"default:galeone"`
  Age        int64  `gorm:"default:18"`
  uuid.UUID  UUID   `gorm:"type:uuid;default:gen_random_uuid()"` // db func
}

CRUD

进行查询操作时,常用db.Where().Find()。Gorm还提供了First等方法,使用这些方法时会返回错误,需要处理。

进行更新操作时,常用db.Model().Update()。还可以使用db.Model().Select().Update()

删除分为物理删除和软删除。

物理删除可以指定id进行删除:db.Delete(&User{}, 10)表示删除id为10的表项。使用db.Where().Delete()可以进行条件删除。

如果类型内部组合了gorm.Model,那么就可以使用Gorm的软删框架。在这种情况下,所有的删除都会变成软删除,数据不会从数据库中被删除。要想查询软删除后的字段,使用db.Unscoped().Find(),要在软删除中使用物理删除,使用db.Unscoped.Delete()

事务

要开始一个事务,使用tx=db.Begin(),随后tx进行Sql操作,最后调用tx.commit()提交事务。当使用tx时,遇到错误一定要调用tx.Rollback()进行事务回滚。

Gorm还提供了db.Transaction()方法,传递一个闭包来防止程序员忘记调用tx.Rollback()。在闭包里不需要调用tx.Rollback(),建议使用。

Hook

Hook是在CRUD之前或之后自动调用的函数。

使用func(*u User)BeforeCreate(tx gorm.DB)(err error)AfterCreate添加Hook。

Gorm性能提高

对于写操作,Gorm会默认封装在事物中运行。为了提高性能,可以调用SkipDefaultTransaction.

使用PrepareStmt缓存预编译语句可以提高后续调用的速度,约为35%。

Gorm生态

工具名工具
代码生成gen
分片库sharding
手动索引hints
乐观锁optimisticlock
读写分离dbresolver
OpenTelementry扩展opentelementry

Kitex

使用IDL定义服务和接口(Thrift、Proto3)。使用Kitex可以根据thrift生成代码kitex -module <NAME> -service <NAME> <NAME>.thrift

Kitex生成代码默认监听8888端口。客户端调用:

for {
        req := &api.Request{Message: "my request"}
        resp, err := client.Echo(context.Background(), req)
        if err != nil {
                log.Fatal(err)
        }
        log.Println(resp)
        time.Sleep(time.Second)
}

Kitex扩展

工具名工具
XDS扩展kitex-contrib/xds
opentelemetry扩展/obsopentelemetry
ECTD服务注册与发现/registry-ectd
......

Hertz

Hertz是一个高性能的Web框架

g := server.Default(server.WithHostPorts("127.0.0.1:8080"))
h.Get("/ping", func(c context.Context, ctx *app.RequestContext)) {
    ctx.JSON(consts.StatusOK, utils.H{"ping" : "pong"})
}
h.Spin()

使用h.+GET POST PUT DELETE等方法进行路由。

参数绑定:

中间件是一个app.HandleFunc,基本流程为:

func(c context.Context, ctx *app.RequestContext) {
    // pre-handle
    c.next(ctx)
    // post-handle  
}

使用h.Use(f())调用中间件。

Hertz也支持客户端。可以使用Hertz的客户端方便地发送请求。

Hertz也支持通过IDL生成基础服务代码。代码工具为Hz