Go Web 三件套 | 青训营笔记

139 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天

一、本堂课重点内容

  • Gorm
  • Kitex
  • Hertz

二、详细知识点介绍:

Gorm

一些注意事项

约定

GORM 倾向于约定优于配置 默认情况下

  • GORM 使用 ID 作为主键
  • 使用结构体名的 蛇形复数 作为表名
  • 字段名的 蛇形 作为列名
  • 并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间

时间和自定义等

First 还是 Find

Find 能查询到多条, First 如果查询不到会返回错误 ErrRecordNotFound

更新非零值

注意, 当使用 struct 进行更新时,GORM 只会更新非零值的字段。 你可以使用 map 更新字段,或者使用 Select 指定要更新的字段

注意字段值为 0, false, 或其他零值的数据

  • 根据 struct 更新属性,只会更新非零值的字段

    db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false}) 
    // UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111; 
    
  • 根据 map 更新属性

    db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false}) 
    // UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
    

Upsert

Upsert

import "gorm.io/gorm/clause"  // Do nothing on conflict 
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user) 

自动 RollBack

建议使用, 防止忘记 Rollback

就是一个 defer

if err = db.Transaction(func(tx *gorm.DB) error {
    ...
}); err != nil {

}

Hook

type User struct {
    ID int64
    Name string `gorm: "default:galeone"`
    Age int64 `gorm:"default: 18"`
}
type Email struct {
    ID int64
    Name string
    Email string
}

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    if u.Age < 0 {
        return errors. New( text; "can't save invalid data")
    }
    return
}

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    return tx.Create (&Email(ID: u.ID,9 Email: u.Name + "@***.com"}).Error
}

Gorm 会自动添加默认事务, 任何 hook 执行过程中报错都会会滚, 可能会造成一些性能问题

性能

  • 对于写操作 (创建、更新、删除),为了确保数据的完整性 Gorm 会将它们封装在事务内运行。但会降低性能,可以使用 SkipDefaultTransaction 关闭默认事务。
  • 使用 PrepareStmt 缓存预编译语句可以提高后续调用的速度 (提高大约 35% 左右)
db, err := gorm.open(mysql.Open("username:password@tcp(locathost:9910)/database?charset=utf8"), &gorm.Config{
    SkipDefaultTransaction: true, // 关闭默认乎务 
    PrepareStnt: true// 开启预编译语句
}
if err != nil { 
    panic("failed to connect database")
}
  • 读写分离(拓展)
  • 结构体复用
  • 链式调用
  • 不加锁

生态

Kitex

快速开始

快速开始

安装代码生成工具

go install github.com/cloudwego/kitex/tool/cmd/kitex @latest

go install github.com/cloudwego/thriftgo @latest

IDL:

Thrift: thrift.apache.org/docs/idl

Proto3 :developers.google.com/protocol-bu…

代码生成:

kitex -module example -service example echo.thrift

  • build.sh 构建脚本
  • kitex_gen 生成的代码
  • main.go 入口
  • handler.go 由用户实现

服务注册与发现

www.cloudwego.io/zh/docs/kit…

使用 etcd 作为注册中心:

github.com/cloudwego/h…

func main() {
	r, err := etcd.NewEtcdResolver([]string{"127.0.0.1:2379"})
	if err != nil {
		log.Fatal(err)
	}
	client := hello.MustNewClient("Hello", client.WithResolver(r))
	for {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
		resp, err := client.Echo(ctx, &api.Request{Message: "Hello"})
		cancel()
		if err != nil {
			log.Fatal(err)
		}
		log.Println(resp)
		time.Sleep(time.Second)
	}
}
  • kitex 自动添加更新缓存, 避免 etcd 的损耗
  • 异步定时更新对象, 删除对象

生态

Hertz

一些特点

  • 丰富的 hook, 例如建立连接后
  • 两个上下文, 一个专门处理元信息, 一个处理请求
  • 支持自定义非标准 Method
  • 自带一个 Http Client, 提供了结构体服用的功能
  • 也能用 IDL 生成 HTTP 接口
    • path, 还是 param 这种也被抽离到了 IDL 里

标准功能

  • 路由: 静态路由 > 命名路由 > 通配路由
  • 参数绑定和校验
  • 中间件
    • 终止中间件调用链的执行
      • c.Abort
      • c.AbortWithMsg
      • c. AbortWithStats

Hertz 性能

  • 网络库 Netpoll
  • Json 编解码 Sonic
  • sync.Pool 复用对象, 协议层数据解析优化(Binding)

拓展: github.com/cloudwego/h…

三、实践练习例子:

四、课后个人总结:

之前一直没注意过, 原来 gin 用的是标准库自带的网络库, Hertz 用了两个上下文挺值得关注

网络库选择

由于网络库触发模式的不同:go net 为 ET 模型,netpoll 为 LT 模型,使得两个网络库的适用场景有一些不同。 在 ET 模型下,由框架处理 Read / Write 事件;在 LT 模型下,由网络库处理 Read / Write 事件。

  • 使得在小包场景下,由于更优的调度策略使得 LT 性能更好;
  • 在大包场景下,由于读 / 写不受框架层控制,使得大量数据被读入内存而不能及时处理,可能会造成内存压力。
  • 在较大 request size 下( request size > 1M ),推荐使用 go net 网络库加流式。
  • 在其他场景下,推荐使用 netpoll 网络库,会获得极致的性能。

五、引用参考:

Gorm 更新多列: gorm.io/zh_CN/docs/…

Hertz: www.cloudwego.io/zh/docs/her…