这是我参与「第五届青训营 」笔记创作活动的第5天
本次课程讲解了Go框架三件套(Gorm、Kitex、Hertz)的安装配置及使用
三件套介绍
- Gorm:
- Gorm 是一个已经迭代了10年+的功能强大的 ORM 框架,在字节内部被广泛使用并且拥有非常丰富的开源扩展。
- Kitex
- Kitex 是字节内部的 Golang 微服务 RPC 框架,具有高性能、强可扩展的主要特点,支持多协议并且拥有丰富的开源扩展。
- Hertz
- Hertz是是字节内部的 Golang 微服务 HTTP 框架,Hertz参考了其他开源框架的优势,结合字节跳动内部的需求,具有高易用性、高性能、高扩展性特点。
Gorm
Gorm作为中继数据的存在,协调内部定义的结构体数据和Mysql等数据库中获取的数据。
Gorm基本操作
Gorm的数据库操作方法都要使用链式调用的方式:db.func1().func2().<...>.ExecuteQuery()
//定义gorm model
type Product struct{
Code string
Price uint
}
//TableName()为gorm框架规定的接口
func(p Product) TableName() string{
return "product"
}
//连接到mysql
db,err := gorm.Open(mysql.Open(url),&gorm.Config{})
//增
db.Create(&Product{Code:"D42",Price:100})
//删
var product Product
db.Delete(&product,1)
//改
var product Product
db.Model(&product).Update("Price",200) //单字段更改
db.Model(&product).Updates(Product{Price:200,Code:"F42"} //仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price":200,"Code":"F42"})
//查
var product Product
db.First(&porduct,1) //根据整型主键查找
db.First(&product,"code=?","D42") //查询Code字段为D42的记录
Gorm目前支持MySQL、SQLServer、PostgreSQL、SQLite等多种类型的数据库
Gorm通过驱动来连接数据库,需要连接其他类型数据库时可以复用/自行开发驱动
//Gorm创建数据
type Product struct{
ID uint `gorm:"primarykey"`
Code string `gorm:"column:code"`
Price uint `gorm:"column:user_id"`
//使用go tag标注了结构体
func main(){
db,err := gorm(mysql.Open(url,&gorm.Config{})
if err != nil {
panic("failed to connect database)
}
products := []*Prodcut{{Code:"D41"},{Code:"D42"},{Code:"D43"}}
res=db.Create(products) //当用primarykey显式定义主键时,创建表操作默认会返回主键
fmt.Println(res.Error) //错误信息包含在res的Error成员中
for _,p:=range products {
fmt.Println(p.ID)
}
}
也可以使用clause.OnConflict来处理冲突,在go tag中使用default关键字在定义默认值。
当使用Create()、First()等方法时,程序已经执行了查询命令,此时再使用Where()方法是不生效的,因此限定范围时要先使用Where()方法
//查询多条数据
users := make([]*User, 0)
users := db.Where("age>10").Find(&users)
查询中的Where()方法包含两个参数,前者为类似于DML语法的字符串,后者为?中的内容,如
"name IN ?" , []string{"jinzhu:,:jinzhu 2"}"name LIKE ?", "%jin%""name = ? AND age >= ?", "jinzhu", "22"
使用Find查找相较于使用First查找是更为安全的,因为使用First如果没有查找到数据会返回错误,而使用Find查找时查找错误只会返回空数组。
使用Struct作为参数时,gorm只会处理非零值
在增删改查时如果对于Find(),Create(),Update(),Updates()等方法没有传递定义为model的结构体指针的话,需要先使用.Model()方法去传递表名
Update()操作也可以更新表达式,使用gorm.Expr()方法即可
gorm的软删方案
type User struct{
ID int64
Name string
Age int64
Deleted gorm.DeleteAt
}
在model结构体的定义时如果添加了类型为gorm.DeleteAt类型的数据,在删除时就不会将数据从数据库中真正删除,但是GORM会将定义的gorm.DeleteAt类型的数据设置为当前时间,并且不能再通过正常的查询获取当前的数据。使用Unscoped可以查询到被软删的数据。
GORM事务
GORM提供了Begin、Commit、Rollback方法用于使用事务
事务可以用来处理对数据一致性要求较强的情况,执行一系列操作后,保证操作要么全部成功,要么全部失败,失败后可以使用回滚机制进行管理
db, err := gorm.Open(mysql(url),&gorm.Config{})
if err != nil{
panic("failed to connect database”)
}
tx := db.begin()
//使用Transaction可以自动提交事务
if err = db.Transaction(func(tx *gorm.DB) error{
if err=tx.Create(&User{Name:"name"}).Error:err!=nil{
tx.Rollback()
return
}
if err=tx.Create(&User{Name:"name1"}).Error:err!=nil{
tx.Rollback()
return
}
return nil
});err !=nil{
return
}
db.begin()操作既开启了一个事务,又固化了对于数据库的链接(因为Go中底层设计了一个链接池用于连接数据库,固化之后可以保证都使用同一个连接),因此必须使用tx作为之后的中继数据变量
GORM性能提高
- SkipDefaultTransaction可以用于关闭写操作中的默认事务(Gorm会给基本操作添加一个默认事务)
- 使用PrepareStmt缓存预编译语句可以提高后续调用的速度
Kitex
RPC(Remote Procedure Call):远程过程调用,指像调用本服务中的函数一样去调用别的服务的函数。
使用IDL定义服务与接口:如果我们要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候,就需要通过 IDL 来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道函数签名一样
使用IDL来调节接口:
namespace go api
struct Request{
1: string message
}
struct Response{
1:string message
}
service Echo{
Response echo(1:Request req)
}
使用kitex -module example -service example echo.thrift命令生成代码
使用client.WithHostPorts()来指定服务端的ip
Hertz
func mian(){
h := server.Default(server.WithHostPorts(url))
h,GET(relative path,func(c context.Context, ctx *app.RequestContext){
ctx.JSON(consts.StatusOK, utils.H{"ping":"pong"})
})
//正常运行时代码会停止在这里
h.Spin()
}
使用Hertz实现,服务监听8080端口并注册了一个GET方法的路由参数
Hertz与Gin等http服务框架不同的是传递了两个上下文,分别用于处理请求(RequestContext)和源信息
Hertz路由
Hertz提供了参数路由和通配路由,参数路由形如:/a/:b,通配路由形如:a/*b,通配路由可以获取之后多段内容,而参数路由只能获取一段内容,其中b值可以被获取如下:
h.GET("hertz/:version",func(ctx context.Context, c*app.RequestContext){
version := c.Param("version")
c.String(consts.StatusOK,"Hello %s", version)
Hertz参数绑定
Hertz中间件
中间件主要用于处理一些统一业务逻辑,用于将业务逻辑和服务端代码解耦。
func MyMiddleware() app.HandlerFunc{
return func(ctx context.Context, c * app.RequestContext){
fmt.Println("pre-handle")
c.Next(ctx) //用于向下执行
fmt.Println("post-handle")
}
}
注册方式:h.use(MyMiddleware())
总结
本节课讲解了实际开发中使用的Go语言三件套:提供对象关系映射的Gorm,提供远程调用作为微服务的Kitex,对外提供api服务的Hertz,总结了框架文档中的要点,并通过实战案例分析将三个框架的使用串联了起来。