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

113 阅读5分钟

一、三件套介绍

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 --versionthriftgo --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 命令生成代码

image1转存失败,建议直接上传图片文件

  • 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.Defaultserver.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…

三、实战案例介绍

四、课程总结