一、三件套介绍
1、Gorm
Gorm是一个已经迭代了 10 年+的功能强大的 ORM 框架,在字节内部被广泛使用并且拥有非常丰富的开源拓展
2、Kitex
Kitex 是字节内部的 Golang 微服务 PRC 框架,具有高性能、强可拓展的主要特点,支持多协议并且拥有丰富的开源拓展。
3、Hertz
Hertz 是字节内部的 HTTP 框架,参考了其他开源框架的优势,结合字节跳动内部的需求,具有高易用性、高性能、高拓展性特点。
二、三件套的使用
1、Gorm 的基础使用
Gorm 的基本使用
package main
//定义gorm model,对应数据库中一张表
type Product struct {
Code string
Price uint
}
//为 model 定义表名
func (p Product) TableName() string {
//返回的字符串就是表名
return "product"
}
func main() {
//连接数据库
db, err := gorm.Open(
mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=uft8mb4&parseTime=True&loc=Local"),
&gorm.Config{})
if err != nil {
panic("failed to connect database")
}
//创建数据
//创建一条数据时传递一个对象
db.Create(&Product{Code: "D42", Price: 100})
//查询数据
var product Product
//一定要传指针,因为gorm要将查询到的字段反写回结构体
//First方法只能查询单条记录
db.First(&product, 1) //根据整形主键查找
db.First(&product, "code=?", "D42") //查找code字段值为D42的记录
//更新数据
db.Model(&product).Update("Price", 200) //将product的price更新为200
//更新多个字段
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) //传结构体时仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
//删除数据
db.Delete(&product, 1)
}
Gorm 的约定(默认):
- Gorm 使用名为 ID 的字段作为主键
- 未定义 TableName 方法时,使用结构体名的蛇形负数作为表名
- 使用字段名的蛇形作为列名
- 使用 CreateAt、UpadteAt 字段作为创建、更新时间
参考文档:gorm.cn/zh_CN/docs/…
GORM 支持的数据库
Gorm 目前支持 MySQL、SQLServer、PostgreSQL、SQLite
参考文档:gorm.cn/zh_CN/docs/…
以连接 SQLServer 数据库为例:
import (
"gorm.io/drvier/sqlserver"
"gorm.io/gorm"
)
//github.com/denisenkom/go-mssqldb
dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
Gorm 通过驱动来连接数据库,如果需要连接其他类型的数据库,可以复用/自行开发驱动
什么是 DSN?参考:github.com/go-sql-driv…
创建数据
参考文档:gorm.cn/zh_CN/docs/…
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Product struct {
ID uint `gorm:"primarykey"`
Code string `gorm:"column:code"`
Price uint `gorm:"column:user_id"`
}
func main() {
db, err := gorm.Open(mysql.Open("username:password@tcp(localhost:9910)/database?charset=utf8"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
//创建一条数据
p := &Product{Code: "D42"}
res := db.Create(p)
fmt.Println(res.Error) //获取err
fmt.Println(p.ID) //返回插入数据的主键
//创建多条数据
products := []*Product{{Code: "D41"}, {Code: "D42"}, {Code: "D43"}}
res = db.Create(products)
fmt.Println(res.Error)
for _, p := range products {
fmt.Println(p.ID)
}
}
开发中常遇到的两种情况:
1、更新数据时遇到唯一索引冲突使用 Upsert,Gorm 提供了 Upsert 的支持
如何使用 Upsert,可以使用 clause.OnConflict 处理数据冲突
//以不处理冲突为例,创建一条数据
p := &Product{Code: "D42", ID: 1}
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)
2、如何使用默认值
通过使用 default 标签为字段定义默认值
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}
查询数据
First 的使用踩坑:
-
使用 First 时,需要注意查询不到数据会返回 ErrRecordNotFound
-
使用 First 查询多条数据,查询不到数据不会返回错误
当使用结构体作为条件查询时,GORM 只会查询非零值字段,这意味着如果你的字段值为 0、''、false 或其他零值,该字段不会被用于构建查询条件,使用 Map 来构建查询条件
更新数据
使用 Struct 更新时,只会更新非零值,如果需要更新零值可以使用 Map 更新或使用 Select 选择字段
2、kitex 的基础使用
安装 Kitex 代码生成工具
Kitex 目前对 Windows 的支持不完善,如果本地开发环境是 Windows 的同学建议使用虚拟机或 WSL2
安装代码生成工具:
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thriftgo@latest
安装成功后,执行 kitex --version 和 thriftgo --version 应该能够看到具体版本号的输出(版本号有差异,以 x.x.x 示例):
$ kitex --version
vx.x.x
$ thriftgo --version
thriftgo x.x.x
参考文档:www.cloudwego.io/zh/docs/kit…
定义 IDL
使用 IDL 定义服务与接口
如果我们要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候就需要通过 IDL 来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道函数签名一样
Thrift:thrift.apache.org/docs/idl
Proto3:developers.google.com/protocol-bu…
namespace go api
struct Request {
1: string message
}
struct Response {
1: string message
}
service Echo {
Response echo(1: Request req)
}
参考文档:www.cloudwego.io/zh/docs/kit…
Kitex 生成代码
使用 kitex -module example -service example echo.thrift 命令生成代码

- build.sh:构建脚本
- kitex_gen:IDL 内容相关的生成代码,主要是基础的 Server/Client 代码
- main.go:程序入口
- handler.go:用户在该文件里实现 IDL service 定义的方法
Kitex 基本使用
服务默认监听 8888 端口
3、Hertz 的基础使用
Hertz 基本使用
参考文档:www.cloudwego.io/zh/docs/her…
使用 Hertz 实现,服务监听 8080 端口并注册了一个 GET 方法的路由函数
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main() {
h := 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()
}
server.Default 和 server.New 都是创建 HTTP 服务器的方法,二者的区别在于:
-
server.Default是创建一个默认的 HTTP 服务器,它会使用net/http包中的默认配置来创建 HTTP 服务器。例如,如果您使用server.Default创建一个HTTP服务器,它将使用默认的最大并发连接数和最大请求大小等属性。 -
server.New是创建一个自定义的 HTTP 服务器,它允许用户自定义 HTTP 服务器的配置。例如,您可以使用server.New来设置 HTTP 服务器的最大并发连接数、最大请求大小等属性。 -
server.New会默认继承一个 recovery 中间件,而server.New不会继承
Hertz 路由
Hertz 提供了 GET、POST、PUT、DELETE、ANY 等方法用于注册路由
参考文档:www.cloudwego.io/zh/docs/her…