Go框架三件套(Gorm/Kitex/Hertz)介绍|青训营笔记

490 阅读5分钟

Go框架三件套(Gorm/Kitex/Hertz)介绍|青训营笔记

这是我参与【第五届青训营】伴学笔记创作活动的第5天。

一、本节课重点内容

  • Gorm的使用方法
  • Kitex的RPC定义方法、代码生成
  • Hertz的路由使用方法、代码生成

二、详细知识点介绍

1.三件套介绍

  • Gorm
    • 迭代10+年的ORM(对象关系映射)框架
    • 字节内部广泛使用,有丰富的开源扩展
  • Kitex
    • 字节内部的Golang微服务RPC框架
    • 高性能,强可扩展
    • 支持多协议、拥有丰富的开源拓展
  • Hertz
    • 字节内部的HTTP框架
    • 高易用/高性能/高扩展

2. 三件套的使用

2.1 Gorm的使用

  • 定义结构体 gorm model

    • 一个简单的gorm model,包含code名和price两个var
    type Product struct
        Code string
        Price uint
    
  • 定义model的表名

    • 一个表名的返回函数
    func (p Product) TableName() string{
        return "product"
    }
    
  • 连接数据库

    • 使用gorm连接数据库,打开mysql数据库,检查错误
    db, err := gorm.Open(
    mysql.open(dsn:"user:password@tcp(127.0.0.1:3306)/dbname?")
    & gorm.Config{}
    )
    if err != nil {
        panic()
    }
    
  • 创建数据、查询数据、更新数据、删除数据

    //创建一个新的gorm model产品结构体,初始化值
    db.Create(&Product{Code:"D42", Price:100})
    
    //查询数据,一定要传递指针
    var product Product
    db.First(&Product, conds:1) //根据整形主key查找(ID为主key)
    db.First(&Product, conds:"code=?","D42")//查询code为D42的Product
    
    //更新数据
    db.Model(&Product).Update(column:"Price", value:200) //单字段
    
    db.Model(&Product).Updates(Product{Price:200, Code:"F42"}) //仅更新非零值字段
    db.Model(&Product).Updates(map[string]interface{}("Price":200,"Code":"F42"})
    
    //删除数据
    db.delete($Product, conds:1)
    
  • Gorm支持的数据库

    • Gorm支持MySQL/SQLServer/PostgreSQL/SQLite
    • 通过驱动连接数据库,需要连接其他类型数据库可以复用、自行开发驱动
    • 使用DSN进行连接
  • Gorm创建数据

    • 使用clause.OnConflict处理数据冲突
    db.Clauses(clause.OnConflict{DoNothing:true})
                     .Create(&p)
    
    • 使用default标签为字段定义默认值
    type User struct{
        ID int64
        Name string 'gorm:"default:galeone"'
        Age int64 'gorm:"default:18"'
    
  • Gorm查询数据

    • First的使用
      • 使用First时需要注意查不到时会返回ErrRecordNotFound
      • 使用Find查询多条数据查不到不会返回错误
    • 使用结构体作为查询条件
      • 使用结构作为条件查询,gorm只查询非0值字段,如果字段值为0、false或其他零值则该字段不会被用于作为查询条件
      • 可以使用map构建查询条件
  • Gorm更新数据

    • 使用struct更新时只会更新非0值
    • 更新零值需要使用map更新或select字段
  • Gorm删除数据

    • 物理删除:Delete
    • 软删除:DeletedAt
      • 拥有软删除能力的model调用delete时,记录不会被从数据库中真正删除,但gorm会将deleteAt置为当前时间,不能通过正常的查询方法找到该数据
      • 可以通过unscoped查询到被软删的数据
  • Gorm事务

    • Begin
      • 固化链接
      • 实行开启sql的语句
      • 返回对象
    • Commit/rollback
    • 提供了Transaction方法用于自动提交事务
      db.Transaction(func(tx *gorm.DB) error())
      
  • Gorm Hook

    • 提供了CURD的Hook能力
    • 在创建、查询、更新、删除等操作之前、之后自动调用的函数
    • 如果任何hook返回错误,Gorm将停止后续操作并回滚事务。
  • 性能提高

    • 写操作保证数据完整性,封装在事务内运行,降低性能
    • 可以使用SkipDefaultTransaction关闭默认事务
    • 使用Prepare Stmt缓存预编译语句可以提高调用速度
  • gorm生态 github.com/go-gorm

    • Gorm代码生成工具 /gen
    • 分片库方案 /sharding
    • 手动索引 /hints
    • 乐观锁 /optimisticlock
    • 读写分离 /dbresolver
    • OpenTelemetry扩展 /opentelemetry

2.2 Kitex的使用

  • 定义IDL

    • 使用RPC需要知道对方的接口是什么,需要传递什么参数
    • 需要知道返回值的样子
    • 通过IDL约定双方的协议,可以知道对方接口的格式
    namespace go api
    
    struct Request{
        1:string message
    }
    
    struct Response{
        1:string message
    }
    
    service Echo {
        Response echo(1:Request req)
    }
    
  • Kitex生成代码

    kitex -module example -service example echo.thrift
    
    • build.sh 构建脚本
    • kitex_gen: IDL内容相关的生成代码,基础的client/server代码
    • main.go 程序入口
    • handler.go 用户在该文件中实现IDL service定义的方法
  • 服务默认监听8888端口

    type EchoImpl struct{}
    
    func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error){
        //Your Code Here
        return
    }
    
  • Client发起请求

    //创建client
    echo.NewClient("example", client.WithHostPorts("0.0.0.0:8888"))
    
    //发起请求
    req := &api.Request(Message: "my request")
    resp, err := c.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
    
  • Kitex服务注册与发现

    • 对接主流服务注册/发现中心,如ETCD、Nacos
    etcd.NewEtcdRegistry()
    etcd.NewEtcdResolver()
    

2.3 Hertz基本使用

  • 实现、服务监听8080端口,注册get方法的路由函数
    func main(){
        h:=server.Default(server.WithHostPorts(hp:"127.0.0.1:8080")
        h.GET(relativePath:"/ping", func(c context.Context, ctx *app.RequestContext){
             ctx.JSON(consts.StatusOK, utils.H{"ping":"pong"})
        })
        h.spin()
    }
    
  • 支持的路由
    //GET
    h.GET(relativePath:"/get", func(c context.Context, ctx *app.RequestContext){}
    
    //POST
    h.POST(relativePath:"/post", func(c context.Context, ctx *app.RequestContext){}
    
    //PUT
    h.PUT(relativePath:"/put", func(c context.Context, ctx *app.RequestContext){}
    
    //DELETE
    h.DELETE(relativePath:"/delete", func(c context.Context, ctx *app.RequestContext){}
    
    //PATCH
    h.PATCH(relativePath:"/patch", func(c context.Context, ctx *app.RequestContext){}
    
    //HEAD
    h.HEAD(relativePath:"/head", func(c context.Context, ctx *app.RequestContext){}
    
    //OPTIONS
    h.OPTIONS(relativePath:"/options", func(c context.Context, ctx *app.RequestContext){}
    
  • 提供了参数路由和通配路由
    • 参数路由 /:version
    • 通配路由 /*action
    • 静态路由>命名路由>通配路由
  • 提供了参数绑定(需要传递指针)
    • bind
    • validate
    • bindandvalidate
  • Hertz中间件(根据注册位置分类)
    • 客户端中间件
    • 服务端中间件
    • 终止中间件调用链的执行 c.Abort(WithMsg/Stats)
  • Hertz Client
    • 提供了HTTP Client用于帮助用户发送HTTP请求
  • Hertz代码生成工具
    • 提供了代码生成工具Hz
    • 通过定义IDL文件生成代码
  • Hertz性能
    • 网络库NetPoll:需要注意切换库
    • Json编解码Sonic:默认使用Sonic
    • 使用sync.Pool复用对象

三、实践练习例子

  • 使用Hertz、Kitex、Gorm搭建的具备一定业务逻辑的后端API笔记项目

    • demoapi:API服务器,HTTP,Gorm/Kitex/Hertz
    • demouser:用户数据管理,protobuf,Gorm/Kitex
    • demonote:笔记数据管理,Thrift,Gorm/kitex
  • 代码例子:见学习资料github。

四、个人总结

本节课主要介绍了Go三件套:Gorm/Kitex/Hertz的基本用法和实现方式。Gorm是go对接数据库的主要方式,其应用有独特的语法需要进行学习。Kitex是RPC的对接实现方式,用户只需定义IDL则可以使用自动代码生成工具进行生成,对接API。Hertz则是HTTP路由的一个实现方式。 本节课学习的内容偏基础使用,需要在实际的项目实践中进行深入学习。

五、参考资料