Go框架三件套 | 青训营笔记

103 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第5天。主要描述了Go开发web后端的三个框架,包括ORM框架Gorm,Web框架Hertz、RPC框架Kitex。

1. Gorm

一个已经迭代了十多年的ORM框架,有着非常丰富的开源拓展。

1.1 一些特性

  • Gorm默认使用名为ID的字段作为主键
  • 默认使用结构体的蛇形复数作为表名,比如XxxYyy默认对应Tag为xxx_yyys
  • 默认使用结构体字段名的蛇形作为列名
  • 使用CreatedAt、UpdatedAt字段作为创建、更新时间 Gorm目前支持的数据库有MySQL、SQLServer、PostgreSQL、SQLite,其他类型的数据库可以复用或自行开发驱动。

1.2 基本使用

链接数据库

db, err := gorm.Open(mysql.Open(dsn string),
                    &gorm.Config{})
if err != nil {
    panic(err)
}

定义表结构体

type TableName struct {
    ID     uint   `gorm:"id"`
    Code   string `gorm:"code"`
    Price  uint   `gorm:"price"`
}

增加数据

//创建一条
p := &TableName{...}
res := db.Create(p)
fmt.Println(res.Error)
fmt.Println(p.ID)
//创建多条
ps := []*TableName{{...},{...}...}
res := db.Create(ps)
fmt.Println(res.Error)
//使用clause.OnConflict处理数据冲突
p := &Product{...}
//不处理冲突
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)

另外可在定义结构体时在tag中通过设置defult标签为字段定义默认值。 查询数据

// 获取第一条记录,查询不到时会返回error,不建议使用
u := &User{}
db.Frist(u)
// 查询多条数据
users := make([]*User, 0)
result := db.Where("age > 10").Find(&users)
results.RowAffected//找到的记录数
results.Error
// 复杂查询
db.Where("name IN ?",[]string{"str1","str2"}).Find(&users)
db.Where("name LIKE ?","%jin%").Find(&users)
db.Where("name = ? AND age >= ?","jinzhu","22").Find(&users)

下面为不常用的查询方式

//使用结构体
db.Where(&User{Name:"jinzhu", Age: 0}).Find(&users)
//使用map
db.Where(map[string]interface{}{"Name":"jinzhu","Age":0}).Find(&users)

使用结构体查询时查询条件只能用非零值字段。 更新数据

//更新单个列
db.Model(&User{ID: 111}).Where("age>?",18).Update("name","hello")
//更新多个列
db.Model(&User{ID: 111}).Updates(User{Name:"hello", Age:18})//只会更新非零值
db.Model(&User{ID: 111}).Updates(map[string]interface{"name":"hello","age":18})
//select选定更新字段
db.Model(&User{ID: 111}).Select("name").Updates(map[string]interface{"name":"hello","age":18})
//SQL表达式更新
db.Model(&User{ID: 111}).Update("age",gorm.Expr("age * ? + ?", 2, 100))

删除数据 Gorm提供了软删除的方式,在定义结构体时增加字段Deleted gorm.DeletedAt便可在调用Delete()时进行软删除,将Deleted字段更新成当前时间,软删除的数据不能通过Find()查询到,可以使用Unscoped()查询

db.Unscoped().Where("age = 20").Find(&users)

1.3 事务

一种基本的写法

tx := db.Begin()//开始事务
if err = tx.Create(&User{Name:"name"}).Error; err != nil {
    tx.Rollback()//回滚
    return
}
...
tx.Commit() //提交事务

为了防止用户忘记写RollbackCommit,可以使用下面一种写法

if err = db.Transaction(func(tx *gorm.DB) error{
    if err = tx.Create(&User{Name:"name"}).Error; err != nil {
        return err //回滚
    }
    ...
    return nil //此处提交
}); err != nil {
    return
}

2. Kitex

是字节内部的Golang微服务RPC框架,具有高性能、可拓展的主要特点。

2.1 安装代码生成工具

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

2.2 IDL

要使用RPC,我们需要接口的参数以及返回的结果,此时需要通过IDL来定义服务与接口,下面是一个简单的示例

namespace go api

struct Request{
    1: string message
}

struct Response{
    1: string message
}

service Echo {
    Response echo(1: Request req)
}

2.3 Kitex生成代码

使用kitex -module example -service example echo.thrift命令来生成代码,文件结构如下

|-- build.sh
|-- echo.thrift
|-- handler.go
|-- kitex_gen
|   '-- api
|       |-- echo
|       |    |-- client.go
|       |    |-- echo.go
|       |    |-- invoker.go
|       |    '-- server.go
|       |-- echo.go
|       '-- k-echo.go
|-- main.go
'-- script
    |-- bootstrap.sh
    '-- settings.py

build.sh是构建脚本;kitex_gen是IDL内容相关的生成代码,基础的Server/Client代码;main.go入口文件;handler.go有IDL service定义的方法,需要用户再此实现。 目前Kitex的注册服务与发现已经对接了主流的服务注册与发现中心,比如ETCD、Nacos等。

3. Hertz

是字节内部的HTTP框架,具有高易用性、高性能、高拓展的特点。

3.1 路由相关

GET、POST、PUT、DELETE等请求的注册路由

h.GET("/get",func(ctx context.Context, c *app.RequestContext) {
    c.String(consts.StatusOK,"get")
})
h.POST(...)
...

定义路由组

v1 := h.Group("/v1")
{
    v1.POST("/login",loginEndpoint)
    v1.POST("/submit",submitEndpoint)
    ...
}

参数路由和通配路由,优先级:静态路由>参数路由>通配路由。

参数路由: "/hertz/:paramName" /hertz不能被匹配
通配路由:"/hertz/*paramName" /hertz能被匹配
c.Param("paramName")获取参数值

3.2 数据绑定

定义结构体,在字段名后添加query、path、header、form、json、vd等tag, 在handler函数中通过c.BindAndValidate()进行数据绑定。

3.3 中间件

通过使用Use(f func(ctx context.Context, c *app.RequestContext))函数来注册中间件,调用Use函数的可以是server也可以是路由组。