没有理想的人不伤心

Golang - 字节三大框架 Gorm Kitex Hertz

2025/03/12
2
0

image.png

简介

Gorm 是 Go 语言的一个强大且易用的 ORM(对象关系映射)库,用于简化与关系型数据库的交互。支持主流数据库, 扩展性强,支持自定义钩子和插件。

  • 模型定义:支持通过结构体定义数据库表模型。
  • CRUD 操作:简化常见的数据库操作,如创建、读取、更新和删除。
  • 查询构建:支持链式查询方法和原生 SQL 查询。
  • 关系管理:支持一对一、一对多、多对多的关系映射。
  • 事务支持:提供简单的事务操作封装。
  • 迁移工具:自动迁移数据库表结构,方便开发。

Kitex 是一个高性能的 Go 语言 RPC 框架,由字节跳动开源,用于开发微服务架构中的 RPC 服务。支持 Thrift、Protobuf 等序列化协议。内置服务注册、负载均衡、熔断、限流和重试机制。

  • 多协议支持:支持 Thrift、Protobuf 等序列化协议。
  • 高性能:通过高效的序列化和传输机制,提供低延迟、高吞吐的服务。
  • 服务治理:内置服务注册、负载均衡、熔断、限流和重试机制。
  • 可扩展性:支持插件化扩展,开发者可以自定义中间件。

Hertz 是一个高性能、高扩展性的 HTTP 服务框架,同样由字节跳动开源,专注于构建高效的 HTTP 服务。

  • 快速路由:内置高效的路由机制,支持参数化路由和分组。
  • 中间件:支持丰富的中间件功能,例如日志、认证、限流等。
  • 性能优化:通过减少内存分配和 GC 压力,实现高效的请求处理。
  • 异步支持:通过协程池优化高并发场景下的资源使用。
  • 开发工具:支持 Swagger 文档生成、代码生成器等开发辅助功能。

Gorm 的使用

Gorm 文档:https://gorm.io/zh_CN/docs/index.html

Gorm 使用链式调用来设置条件等,因此条件的设置必须要在执行之前。Find、Update、Delete 等方法才是真正的执行函数。

基本使用

1732078756141-1cead379-30f6-40a9-9561-3f242b4597ca.png

DSN(Data Source Name)数据源名称

参数介绍:[https://github.com/go-sql-driver/mysql#dsn-data-source-name](https://github.com/go-sql-driver/mysql#dsn-data-source-name)

通用格式:

[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]

Gorm 中的约定俗成:

  • 默认使用 ID 字段作为主键
  • 未定义表名,默认使用结构体 gorm model名的蛇形复数形式作为表名,如: 结构体 ProductCategory 的默认表名是 product_categories
  • 默认使用字段名的蛇形作为列名
  • 使用CreatedAtUpdatedAt字段作为创建时间和更新时间

数据库连接

Gorm 支持的数据库: MySQLPostgreSQLSQLiteSQL Server

通过驱动来连接数据库,要连接其他类型的数据库,则需要自行开发/复用开发驱动

以 sql server 数据库连接为例,

1732079534392-dbcb5070-906d-4c74-9df1-8b27fdd473d6.png

创建数据

1732081697119-fefb79c0-7455-403f-8371-fc4614830c02.png

  1. 创建Gorm model

使用 Gorm tag(反引号)来标记,设置字段名、主键、默认值等

  1. 连接数据库
  2. 创建数据

upsert:表中存在指定值则更新,不存在则添加

可以使用clause.OnConflict来处理创建数据时的数据冲突

1732081429216-d04ff028-18c0-4b7b-8661-d0a300ef242d.png

DoNothing表示不处理冲突

使用tag设置字段默认值:

1732081516328-8485f73a-cbb4-4814-a1a8-a10b11c571bf.png

查询数据

1732081960646-b7f0be06-37e4-4a62-9539-be86fc7d171d.png

注意事项:

  • 使用First时,查询不到数据会返回ErrRecordNotFound,而使用Find查询不到,不会返回错误

因此更多使用Find

  • 使用结构体作为查询条件时,GORM 只会查询非零值字段。这意味着如果您的字段值为0、""、 false或其他零值,该字段不会被用于构建查询条件,使用 Map 来构建查询条件则会加上零值的字段

更新字段

1732082812993-540235d7-7517-48af-b637-43d5361edbd2.png

更新字段时,要使用 Model()来指定表名,特别是更新单个列时!当 updates中传递了表名,则不需要 Model了,但是最好都加上

同样的,使用 Struct 更新时,只会更新非零值,如果需要更新零值,可以使用 Map 更新或使用 Select 选择字段。

删除数据

物理删除

1732083373716-ea8cad36-543b-4d0e-9524-766c1c3e76ce.png

软删除

在表结构体中需要额外定义一个 gorm.DeletedAt字段,来表明软删除

1732083414919-9143c4d1-10cd-4841-b3eb-7799a39ab371.png

定义了软删除字段后,调用 Delete时,并没有从磁盘中删除掉,只是在 deleted_at字段中设置删除时间,并且不能通过正常查询方法来查询到软删除的数据

查询软删除数据需要使用 Unscoped方法

Gorm 事务

Gorm 提供了 BeginCommitRollback

1732084635425-5a360577-6c5e-4007-91c6-be4cce356930.png

注意:

Begin开启事务后,会返回当前数据库的固化链接(和 db 一样都是 db 对象),在事务中应该使用该返回值,而不是 db

更好的方式:

Gorm 中提供了Transaction方法用于自动提交事务,出错时回滚事务,避免了 commit 和 rollback 的漏写

1732084942023-bf3245d9-db68-4cbf-9e31-be05f6f90729.png

Gorm Hook

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

1732085179932-1e5506be-ce36-4ff2-b93b-27df95d5ba94.png

在 Hook 操作中,会与中间的操作开启同一个默认事务,以保证数据的一致性。

若在 Hook 中或语句执行中返回错误,Gorm 会停止后续操作并进行事务回滚

性能提升

在进行更新、创建、删除操作时,Gorm 会为该语句自动开启一个默认事务,这会降低性能

  • 可以使用SkipDefaultTransaction来关闭默认事务
  • 使用PrepareStmt缓存预编译语句,可以提高后续调用速度,提升大约 35%的性能

1732085691416-a425a47a-bbd6-404a-8dc4-c2571c5aec5b.png

Gorm 生态

1732086235885-6ace82c6-9b9b-4461-9021-b79a366281f2.png

乐观锁悲观锁是两种在数据库中控制并发访问的方法,用于保证数据的一致性和正确性。它们的核心区别在于对资源冲突的处理策略:

  • 乐观锁:假设冲突很少,只有在提交时检测冲突。
  • 悲观锁:假设冲突频繁,操作前先锁定资源以防冲突。

1732086555255-9639c128-8be2-42de-bb97-91b3318b4ae5.png

Kitex

官方文档:https://www.cloudwego.io/zh/docs/kitex/getting-started/

kitex 服务默认监听 8888 端口

定义 IDL

IDL(Interface Definition Language):是一种用来定义服务接口及其数据结构的语言

主要作用:

  • 定义服务接口:描述 RPC 服务中的方法名称、参数类型和返回类型。
  • 定义数据结构:描述服务方法中使用的数据类型。
  • 生成代码:通过工具生成客户端和服务端的代码框架,减少手动编写的工作量。
  • 跨语言支持:使用统一的定义,使不同语言的系统能够互相通信。

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

下面这个是 thrift 协议的 IDL,不同协议,IDL 规则不同

1732089714234-26e2cda7-ab7b-4889-b2d9-58ff0dbd46b4.png

Kitex 生成代码

使用下面的命令通过 thrift 的 IDL 文件生成代码

kitex -module example -service example echo.thrift

生成文件如下:

1732090385935-6518089d-a3b4-4602-bf49-4196114b79f0.png

build.sh:构建脚本,用于构建可执行文件

kitex_gen:IDL 内容相关的生成代码,主要是基础的服务器端和客户端代码

main.go:程序入口

handler.go:在该文件中实现 IDL server 定义的方法

handler.go中自动生成的内容

1732090771686-fe041857-1646-4f0b-ae8b-029bab0a8055.png

在其中编写代码逻辑就可以了

创建 client,发起请求

创建 RPC 的客户端,只要客户端和服务端所使用的协议一致就可以(thrift、gRPC)

1732091008607-cba33eb0-5d6a-4d89-821f-404d649e60eb.png

WithHostPorts指定 ip 和端口,但是这样的话,服务发现就会失效

使用微服务时,更多的是使用服务发现与注册

发起 RPC 请求

1732091139545-152a606a-3549-48cf-9e31-6a4dfabf7c86.png

在 req 中写入要处理的请求逻辑代码

其他代码 kitex 已经自动生成了,直接调用Echo就可以

Kitex 服务注册与发现

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

通过把server注册到服务注册中心,client到服务中心获取数据

服务端:

1732091690186-e6ba002b-b5ac-448d-80cb-7afea5ba4421.png

上述代码将 server 注册到 ETCD 服务中心,并 run 该服务:

NewEtcdRegister使用 ETCD 扩展时,要指定 ETCD 集群的地址和端口

使用NewServer创建一个服务,然后Run,这样 server 就被注册到了 ETCD 中

客户端:

客户端发现服务的代码

1732092684493-003cf554-e927-4371-bec8-c3193cb6ff3d.png

NewEtcdResolver创建服务发现对象,参数传递 ETCD 集群的地址,此处的 err 必须要处理

MustNewClientNewClient传入服务名来过滤集群中的服务,并传入服务发现对象

然后设置超时并发送请求

Kitex 生态

1732093085106-b929d74a-0e44-45f6-8070-863c071fd27a.png

XDS:用于流量路由

Hertz

Hertz 的基本使用

1732093592773-04d62331-7e4f-4f1d-b1f6-fdf464abb97b.png

上面的代码,创建服务并监听 8080 端口并注册了一个 GET 方法的路由函数

server.Default或者server.New都可以创建一个 Hertz 对象

区别:Default默认继承recover中间件,New没有继承

GET注册 get 路由,其中有两个上下文,一个用于传递原信息,一个专注于请求的处理

Hertz 路由

Hertz 提供了标准的 http 方法的路由注册

1732098578369-07d58cf1-88a2-45e6-bd23-d53fe695c7dc.png

Hertz 支持路由分组

1732098692180-75b41d38-34f5-409c-9bd5-fe7594ce9a0e.png

支持参数路由通配路由,路由优先级为:静态路由 > 命名路由 > 通配路由

1732098923415-3dd5792b-5ebb-4923-b055-a88f4db867e9.png

Hertz 参数绑定

Hertz 提供了BindValidateBindAndValidate用于进行参数绑定和校验

参数绑定是用于从 HTTP 请求中提取参数并将其解析到结构体或变量的一种功能。

1732099330361-1d35b87f-cf5e-4971-aae6-a5e31f16d4a4.png

Hertz 中间件

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

如下面这个简单的中间件代码

1732100417078-ec67f53c-721f-422a-9643-905db7d8001d.png

多个中间件使用洋葱模型调用,通过Next调用下一个

Use注册中间件

Hertz Client

Hertz 也提供了客户端的功能,用户帮助用户发送请求

1732100702486-ed5c10b3-82a8-43f4-9040-baebe6a1fa81.png

Hertz 代码生成

Hertz 提供了代码生成工具 Hz,通过定义 IDL(interface description language)文件即可生成对应的基础服务代码,包括服务端代码和客户端代码

如以下 IDL 文件

1732100969628-890fac47-0ac6-4c12-84fb-a4656a083a94.png

生成文件目录结构

1732101017235-71f116e0-a9c6-4139-9ccb-1facb2411d65.png

main.go启动文件

router.gorouter_gen.go是注册路由

下面是生成的 hello_service.go文件

1732101254092-90f36233-5a63-4946-ad5b-0512c1c73fa9.png

Hertz 生态

1732101550739-1fc8ad70-d44a-4b67-b299-8f6815c728a1.png