这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
1 Gorm
Gorm是一款ORM框架,ORM是“对象-关系-映射”的简称,Go语言中常用的ORM框架有很多,如gorm、facebook-ent、xorm、upper/db、gorose等,这里以gorm为例。
1.1 Gorm介绍
Gorm 是 Golang 的一个 orm 框架。ORM 是通过实例对象的语法,完成关系型 数据库的操作,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM 框架可以让我们更方便的操作数据库。
Gorm官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
1.2 功能
-
Create,Save,Update,Delete,Find 中钩子方法
-
支持 Preload、Joins 的预加载
-
事务,嵌套事务
-
Context、预编译模式、DryRun 模式
-
批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
-
等
1.3 数据库连接
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// loc 设置本地系统的时间, 前提 parseTime=True
// 更多参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name
_, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
//db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger:logger.Default.LogMode(logger.Info)}) 打印sql日志
if err != nil {
panic(err) // 如果数据库不存在会报错
}
}
1.4 CRUD
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
// 日志配置
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别为info
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 彩色打印
},
)
dsn := "root:123@tcp(127.0.0.1:3306)/gorm_test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err) // 如果数据库不存在会报错
}
//1 迁移 表,可以写多个
//db.AutoMigrate(&Product{})
//2 插入数据
db.Create(&Product{Code: "D42", Price: 100})
db.Create(&Product{Code: "D43", Price: 150})
//3 查询数据
var product Product // 定义空Product结构体对象
db.First(&product, 1) // 根据整型主键查找
db.First(&product, "code = ?", "F43") // 查找 code 字段值为 D43 的记录
//fmt.Println(product)
//4 更新 - 将 product 的 price 更新为 200
db.Model(&product).Update("Price", 200)
// Update - 更新多个字段
db.Model(&product).Updates(Product{Price: 200, Code: "F43"}) // 仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 300, "Code": "F42"})
//5 Delete - 删除 product
db.Delete(&product, 1) // 软删除
}
1.5 事务
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
2 Kitex
Kitex 是一款强可扩展性的 Go RPC 框架。
架构图如下:
2.1 安装
go install github.com/cloudwego/kitex/tool/cmd/kitex@latest
go install github.com/cloudwego/thriftgo@latest
2.2 server
创建IDL文件
Kitex 典型的使用场景就是基于 thrift 定义的 IDL 来定义服务接口,实现客户端和服务端的通信。
这里我们新建一个demo的服务定义: kstudy.thrift
namespace go api
struct Request {
1: string message1
2: string message2
}
struct Response {
1: string message
}
service KStudy {
Response Concat(1: Request req)
}
可以看到,我们定义了一个KStudy服务,包含一个Concat接口,语义是将 Request 中的 message1 和 message2 两个字符串拼接起来,通过 Response 返回。
基于thrift生成代码,所以只需要在项目目录运行
kitex -service kstudy kstudy.thrift
2.3 client
我们在 kstudy 下新建client文件夹,用于实现客户端相关逻辑。新增 main.go 作为客户端的启动入口。由于我们目前只是单机的 server 和 client 交互,没有复杂的服务发现逻辑,需要通过添加client.Option来指定调用的 host 和 port。
package main
import (
"context"
"log"
"github.com/ag9920/kstudy/kitex_gen/api"
"github.com/ag9920/kstudy/kitex_gen/api/kstudy"
"github.com/cloudwego/kitex/client"
)
func main() {
client, err := kstudy.NewClient("kstudy", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
log.Fatal(err)
}
req := &api.Request{
Message1: "message1",
Message2: "message2",
}
resp, err := client.Concat(context.Background(), req)
if err != nil {
log.Fatal(err)
}
log.Println(resp)
}
3 Hertz
Hertz[həːts] 是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttp、gin、echo 的优势, 并结合字节跳动内部的需求,使其具有高易用性、高性能、高扩展性等特点,目前在字节跳动内部已广泛使用。 如今越来越多的微服务选择使用 Golang,如果对微服务性能有要求,又希望框架能够充分满足内部的可定制化需求,Hertz 会是一个不错的选择。
3.1 安装Hertz
go install github.com/cloudwego/hertz/cmd/hz@latest
3.2 使用
执行hz new初始化项目,项目初始化后,hertz会自动创建对应项目文件,文件结构如下
.
├── biz
│ ├── handler
│ │ └── ping.go
│ └── router
│ └── register.go
├── go.mod
├── main.go
├── router.go
└── router_gen.go
3.3 获取请求参数
通过c.Query获取路径参数
func Person(ctx context.Context, c *app.RequestContext) {
name := c.Query("name")
c.JSON(200, utils.H{
"data": name,
})
}
通过c.Param获取路径参数
func HelloPerson(ctx context.Context, c *app.RequestContext) {
name := c.Query("name")
age := c.Param("age")
c.JSON(200, utils.H{
"age": age,
"name": name,
})
}
3.4 路由注册
r.GET("/person/:age", hello.HelloPerson)