day5 - GORM & Kitex & Hertx | 青训营笔记

98 阅读4分钟

这是我参加青训营的第 5 天

Gorm

基本使用

创建表

type Product struct {
    ID      uint    `gorm:"primarykty"`     // 设置表主键
    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{})     // charset指的是编码格式
    
    // 创建一条数据
    p := &Product{Code: "D42"}
    res := db.Create(p)
    // 创建多条数据
    products := []*Product{{Code: "D41"}, {Code: "D42"}, {Code: "D43"}}
    res := db.Create(products)
    
}

如何使用默认值

type User struct {
    ID      int64
    Name    string  `gorm:"default: galeone"`   // 默认值
    Age     int64   `gorm:"default: 18"`        // 默认值
}

查询数据

// 获取第一条数据(主键升序),若查询不到则返回 ErrRecorNotFound
u := &User{}
db.First(u)     // select * from u order by id limit 1
​
// 查询多条数据
users := make([]*User, 0)
db.Where("Age > 10").Find(&users)   // select * from users where Age > 10
​
fmt.Println(result.RowsAffected)    // 返回找到的记录数,相当与`len(users)`
​
db.where("name IN ?", []string{"zhangsan", "lisi"}).Find(&users)
db.where("name LIKE ?", "%a%").Find(&users)
db.where("name = ? AND age = ?").Find(&users)
​
db.where(&User{Name: "zhangsan", Age: 0}).Find(&users)
db.where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)

注意:使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果字段值为0、“”、false 或其他零值该字段不会被用于构建查询条件,使用 Map 来构建查询条件

更新数据

注意:使用 Struct 更新时,只会更新非零值,如果需要更新零值可以使用 Map 更新或使用 Select 选择字段

删除数据

物理删除

软删除

注意:GORM 提供了 gorm.DeletedAt 用于帮助用户实现软删除

拥有软删除能力的 Model 调用 Delete 时,记录不会被从数据库中真正删除。 但 GORM 会将 DeletedAt 置为当前时间,并且你不能再通过正常的查询方法找到该记录。

使用 Unscoped 可以查询到被软删除的数据

事务

需要通过事务保证数据的一致性

Gorm 提供了 Begin、Commit、Rollback 方法用于使用事务

Begin():先固化连接,为了保证使用事务的时候是同一个连接,因为Golang对数据库操作使用的是连接池,然后执行开启事务的sql语句

Gorm 提供了 Tansaction 方法用于自动提交事务,防止用户因为漏写Commit、Rollback而引发的事故

db.Transaction(func(tx *gorm.DB) error {})

Hook

Gorm 在提供了 CURD 的 Hook 能力

Hook 是在创建、查询、更新、删除等操作之前、之后自动调用的函数。

如果任何 Hook 返回错误,GORM 将停止后续的操作并回滚事务

性能提高

对于写操作(创建、更新、删除) ,为了确保数据的完整性, GORM 会将他们封装在事务内运行但这会降低性能,可以使用 SkipDefaultTransaction 关闭默认事务

使用 PrepareStmt 缓存预编译语句可以提高后续调用的速度

db, err := gorm.Open(mysql.Open("username:password@tcp(localhost:9910)/database?charset=utf8"), &gorm.Config{
    SkipDefaultTransaction: true,   // 关闭默认事务
    PrepareStmt:            true    // 缓存预编译语句
})

Kitex

Kitex 目前对 Windows 的支持不完善,Windows 建议使用虚拟机

安装代码生成工具

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

go install github.com/cloudwego/thriftgo@latest

定义IDL

用途如果我们要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候,就需要通过 IDL 来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道函数签名一样

简单定义一个IDL:

namespace go api
​
struct Request {
    1: string message
}
​
struct Response {
    1: string message
}
​
service Echo {
    Response echo(1: Request req)
}

生成代码

build.sh:构建脚本

kitex_gen:内容相关的生成代码,主要是基础的 Server/Client 代码

main.go:程序入口

handler.go:用户在该文件里实现 IDL service 定义的方法

基本使用

服务默认监听8888端口

发起请求

  • 创建 Client

  • 发送请求

服务发现与注册

当使用微服务的时候通常使用服务发现与注册调用下游微服务

目前 Kitex 的服务注册与发现已经对接了主流的服务注册与发现中心,如ETCD, Nacos等

Kitex 底层实现会加一个缓存,并且后台会有一个更新对象逻辑和删除对象逻辑,防止对象失效。

Hertz

基本使用

使用 Hertz 实现,服务监听 8080 端口并注册了一个 GET 方法的路由函数

package main
​
import (...)
​
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: 会集成一个中间件Recover

路由

Hertx 提供了 GET、POST、PUT、DELETE、ANY 等方法用于注册路由

Hertz 提供了路由组(Group)的能力,用于支持路由分组的功能

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

Hertz 提供了参数路由和通配路由,路由的优先级为:静态路由 > 命名路由 > 通配路由

Hertz 提供了 Bind、Validata、BindAndValidate 函数用于进行参数绑定和校验

中间件

Hertx 的中间件主要分为客户端中间件服务端中间件

  • 终止中间件调用链的执行函数

    • c.Abort
    • c.AbortWithMsg
    • c.AbortWithStats

展示服务端中间件:

h.Use(MyMiddleware()) :注册全局中间件,每个路由前后都打印

Hertz 提供了 HTTP Client 用于帮助用户发送 HTTP 请求

代码生成工具

Hertz 提供了代码生成工具 Hz,通过定义 IDL 文件即可生成对应的基础服务代码

namespace go hello.examplestruct HelloReq {
    1: string Name (api.query="name");
}
​
struct HelloResp {
    1: string RespBody;
}
​
service HelloService {
    HelloResp HelloMethod(1: HelloReq request) (qpi.get="/hello")
}