这是我参与「第五届青训营 」伴学笔记创作活动的第 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
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 由用户实现
服务注册与发现
使用 etcd 作为注册中心:
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 的损耗
- 异步定时更新对象, 删除对象
生态
-
- 多泳道, 流量路由, 不同的流量打到不同的服务, 例如多套测试环境
-
- 可观测性, 三件套都有, 监控日志打通
- 分别记录 sql, rpc 调用时长
Hertz
一些特点
- 丰富的 hook, 例如建立连接后
- 两个上下文, 一个专门处理元信息, 一个处理请求
- 支持自定义非标准 Method
- 自带一个 Http Client, 提供了结构体服用的功能
- 也能用 IDL 生成 HTTP 接口
- path, 还是 param 这种也被抽离到了 IDL 里
标准功能
- 路由:
静态路由 > 命名路由 > 通配路由 - 参数绑定和校验
- 中间件
- 终止中间件调用链的执行
- c.Abort
- c.AbortWithMsg
- c. AbortWithStats
- 终止中间件调用链的执行
Hertz 性能
- 网络库 Netpoll
- Json 编解码 Sonic
- sync.Pool 复用对象, 协议层数据解析优化(Binding)
三、实践练习例子:
四、课后个人总结:
之前一直没注意过, 原来 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/…