GoWeb框架|青训营笔记

110 阅读8分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第5天,今天学习了GoWeb框架相关的知识下面是我的笔记

Go主要框架(orm,http,rpc)

1、Gorm框架

1.1、orm

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM是连接数据库的桥梁,只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中,简单来讲,orm是简化数据持久化操作,对数据库数据进行封装便于后端编程的技术模式。

在Java领域,主要的orm框架有全自动orm框架JPA和半自动orm框架MyBatis。区别在于前者几乎完全封装了sql语句,将写sql变成和调API接口一般简单,而后者则需要手动编写SQL到xml文件中。Go的Gorm框架更贴切于JPA,本身封装好了大量的SQL语句,几乎可以实现所有的单表查询。

1.2、Gorm的基础使用

  • go module下获取该框架
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
  • learnku.com/docs/gorm/v… Gorm中文文档地址

  • Gorm使用结构体封装数据库表,结构体名对应表名,结构体属性对应表字段,约定优先于配置。默认情况下,GORM 使用 ID 作为主键使用结构体名的 蛇形复数 作为表名字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间

    type User struct {
      CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
      UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
      Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳填纳秒数充更新时间
      Updated   int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间
      Created   int64 `gorm:"autoCreateTime"`      // 使用时间戳秒数填充创建时间
    }
    
    • 时间戳的合理使用可以帮助我们解决很多时间类问题。

    • 可以将模板结构体单独定义,然后嵌入业务结构体,做到解耦合和可复用

    • type User struct {
        gorm.Model
        Name string
      }
      // 等效于
      type User struct {
        ID        uint           `gorm:"primaryKey"`
        CreatedAt time.Time
        UpdatedAt time.Time
        DeletedAt gorm.DeletedAt `gorm:"index"`
        Name string
      }
      
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"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

其中Config{}中可以写一些相关配置,具体见文档。

1.3、Crud

1.3.1.insert增加操作

  • 单条数据插入:

  • - user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
    - result := db.Create(&user) // 通过数据的指针来创建
    db.Select("Name", "Age", "CreatedAt").Create(&user)
    // INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
    只插入部分字段,其他字段默认
    db.Omit("Name", "Age", "CreatedAt").Create(&user)
    // INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
    忽略这些字段,进行插入。
    
  • 批量插入:

  • var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
    db.Create(&users)
    

    总结就是单数据插入用类封装,批量插入用类的切片数组封装,当然要求都是指针操作,不要忘记&

  • map方式插入

  • GORM 支持根据 map[string]interface{}[]map[string]interface{}{} 创建记录,例如:

    db.Model(&User{}).Create(map[string]interface{}{
      "Name": "jinzhu", "Age": 18,
    })
    

    推荐数据量比较多,字段较完整用结构体包装类插入,只插入部分值可以考虑map

1.3.2.select查询操作,**重点

  • 查单个对象,返回一条数据

  • ORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误,其中take为随机查询。

  • db.First(&user, 10)
    // SELECT * FROM users WHERE id = 10;
    
    db.Find(&users, []int{1,2,3})
    // SELECT * FROM users WHERE id IN (1,2,3);
    这里都是默认主键查询
    

    这里要格外注意SQL注入的问题,当实际编程时,10所在的地方往往是前端传过来的某个请求参数,此时应当使用占位符?预编译而不是Spritf字符串拼接

  • 查询所有//不建议调这个方法,底层似乎是select *

  • db.Find(&users)

  • 条件查询,db.Where()

  • learnku.com/docs/gorm/v… 这一部分具体内容参考文档即可。

  • type User struct {
      ID     uint
      Name   string
      Age    int
      Gender string
      // 假设后面还有几百个字段...
    }
    
    type APIUser struct {
      ID   uint
      Name string
    }
    
    // 查询时会自动选择 `id`, `name` 字段
    db.Model(&User{}).Limit(10).Find(&APIUser{})
    // SELECT `id`, `name` FROM `users` LIMIT 10
    

    用这种方法代替db.Find()会更好

  • 加行级锁update

  • db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users) // SELECT * FROM users FOR UPDATE

db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)

var age int
db.Raw("select sum(age) from users where role = ?", "admin").Scan(&age)

原生sql,挺好的功能,就笔者而言,如果涉及到较复杂条件的查询,使用原生SQL语句会更为适应。

1.3.3.update更新

  • 更新操作的两种手法

  • 第一种

  • db.First(&user)
    user.Name = "jinzhu 2"
    user.Age = 100
    db.Save(&user)
    // UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
    

    先查询单条数据,封装到user对象中,然后在修改user对象,映射回数据库

  • 第二种:

  • db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
    // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
    

    直接条件查询。

  • 更新选定字段,通过Select保证只更新选定字段

  • db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
    // UPDATE users SET name='hello' WHERE id=111;
    
  • 更多更新操作可查看文档

1.3.4.delete删除

  • 条件删除

  • db.Delete(&User{}, 10)
    // DELETE FROM users WHERE id = 10;
    
    db.Delete(&User{}, "10")
    // DELETE FROM users WHERE id = 10;
    
    db.Delete(&users, []int{1,2,3})
    // DELETE FROM users WHERE id IN (1,2,3);
    
  • 批量删除

  • db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
    // DELETE from emails where email LIKE "%jinzhu%";
    
    db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
    // DELETE from emails where email LIKE "%jinzhu%";
    
  • 软删除,模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),将自动获得软删除的能力。

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

1.4、Gorm常用相关组件

代码生成工具github.com/go-gorm/gen
手动索引github.com/go-gorm/hints
乐观锁github.com/go-gorm/optimisticlock
读写分离github.com/go-gorm/dbresolver
OpenTelemetrygithub.com/go-gorm/opentelmetry

2、Hertz框架

2.1基本使用方法

  • 安装命令行工具go install github.com/cloudwego/hertz/cmd/hz@latest
  • hz -v验证安装情况
  • 进入某目录内,使用hz命令init项目:hz new -mod [模块名],项目目录结构如下图。。。
  • D:. │ .gitignore │ .hz │ go.mod │ go.sum │ main.go │ router.go │ router_gen.go │ └─biz ├─handler │ ping.go │ └─router register.go

2.2、网络库

Hertz 默认集成了 Netpoll 和 Golang 原生网络库 两个网络库,用户可以根据自己的场景选择合适的网络库以达到最佳性能。

对于 Server 来说,默认使用 netpoll ,可以通过配置项进行更改:

server.New(server.WithTransport(standard.NewTransporter))
server.New(server.WithTransport(netpoll.NewTransporter))

对于 Client 来说,可以通过配置项进行更改:

client.NewClient(client.WithDialer(standard.NewDialer()))
client.NewClient(client.WithDialer(netpoll.NewDialer()))

2.3、路由

Hertz 支持丰富的路由类型用于实现复杂的功能,包括静态路由、参数路由、通配路由。

路由的优先级:静态路由 > 命名路由 > 通配路由

  • 下表为主要的路由注册方法
Hertz.GET用于注册 HTTP Method 为 GET 的方法
Hertz.POST用于注册 HTTP Method 为 POST 的方法
Hertz.DELETE用于注册 HTTP Method 为 DELETE 的方法
Hertz.PUT用于注册 HTTP Method 为 PUT 的方法
Hertz.PATCH用于注册 HTTP Method 为 PATCH 的方法
Hertz.HEAD用于注册 HTTP Method 为 HEAD 的方法
Hertz.OPTIONS用于注册 HTTP Method 为 OPTIONS 的方法
Hertz.Handle这个方法支持用户手动传入 HTTP Method 用来注册方法,当用于注册普通的 HTTP Method 方法时和上述的方法作用是一致的,并且这个方法同时也支持用于注册自定义 HTTP Method 方法
Hertz.Any用于注册所有 HTTP Method 方法
Hertz.StaticFile/Static/StaticFS用于注册静态文件

3、Kitex框架

3.1、Kitex简介

Kitex是字节跳动内部的 Golang 微服务 RPC 框架,具有高性能强可扩展的特点,在字节内部已广泛使用。如果对微服务性能有要求,又希望定制扩展融入自己的治理体系,Kitex 会是一个不错的选择。

  • 高性能

    使用自研的高性能网络库 Netpoll,性能相较 go net 具有显著优势。

  • 扩展性

    提供了较多的扩展接口以及默认扩展实现,使用者也可以根据需要自行定制扩展,具体见下面的框架扩展。

  • 多消息协议

    RPC 消息协议默认支持 ThriftKitex ProtobufgRPC

  • 多传输协议

    传输协议封装消息协议进行 RPC 互通,传输协议可以额外透传元信息,用于服务治理,Kitex 支持的传输协议有 TTHeaderHTTP2。TTHeader 可以和 Thrift、Kitex Protobuf 结合使用;HTTP2 目前主要是结合 gRPC 协议使用,后续也会支持 Thrift。

  • 多种消息类型

    支持 PingPongOneway双向 Streaming。其中 Oneway 目前只对 Thrift 协议支持,双向 Streaming 只对 gRPC 支持,后续会考虑支持 Thrift 的双向 Streaming。

  • 服务治理

    支持服务注册/发现、负载均衡、熔断、限流、重试、监控、链路跟踪、日志、诊断等服务治理模块,大部分均已提供默认扩展,使用者可选择集成。

  • 代码生成

    Kitex 内置代码生成工具,可支持生成 ThriftProtobuf 以及脚手架代码。

3.2、基础使用

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

    分别安装kitex工具和thriftgo工具

  • 快速开始 | CloudWeGo kitex文档。