本篇内容介绍了“Go框架三件套Gorm、Kitex、Hertz怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
Gorm、Kitex、Hertz的基本用法与常见的API讲解
gorm是Golang语言中一个已经迭代数十年且功能强大、性能极好的ORM框架
ORM:Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,
简单来说,在golang中,自定义的一个结构体对应着一张表,结构体的实例则对应着表中的一条记录。
Kitex是字节内部Golang微服务RPC框架 具有高性能、强可扩展的主要特点 支持多协议并且拥有丰富的开源扩展
Hertz是字节内部的Http框架 参考了其他开源框架的优势 结合字节跳动内部的需求 具有高可用、高性能、高扩展性的特点
该部分笔记主要参考:gorm.io/zh_CN/docs
模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成
- type User struct {
- ID uint
- Name string
- Email *string
- Age uint8
- Birthday *time.Time
- MemberNumber sql.NullString
- ActivatedAt sql.NullTime
- CreatedAt time.Time
- UpdatedAt time.Time
- }
GORM 倾向于约定优于配置
默认情况下,GORM 使用 ID
作为主键,使用结构体名的 蛇形复数
作为表名,字段名的 蛇形
作为列名,并使用 CreatedAt
、UpdatedAt
字段追踪创建、更新时间
GORM 定义一个 gorm.Model
结构体,其包括字段 ID
、CreatedAt
、UpdatedAt
、DeletedAt
- // gorm.Model 的定义
- type Model struct {
- ID uint `gorm:"primaryKey"`
- CreatedAt time.Time
- UpdatedAt time.Time
- DeletedAt gorm.DeletedAt `gorm:"index"`
- }
还可以将它嵌入到结构体中,以包含这几个字段,例如:
- 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
- }
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
- import (
- "gorm.io/driver/mysql"
- "gorm.io/gorm"
- )
- func main() {
- // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
- 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{})
- }
注意: 想要正确的处理 time.Time
,您需要带上 parseTime
参数, 要支持完整的 UTF-8 编码,您需要将 charset=utf8
更改为 charset=utf8mb4
GORM 允许通过一个现有的数据库连接来初始化 *gorm.DB
- import (
- "database/sql"
- "gorm.io/driver/mysql"
- "gorm.io/gorm"
- )
- sqlDB, err := sql.Open("mysql", "mydb_dsn")
- gormDB, err := gorm.Open(mysql.New(mysql.Config{
- Conn: sqlDB,
- }), &gorm.Config{})
- user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
- result := db.Create(&user) // 通过数据的指针来创建
- user.ID // 返回插入数据的主键
- result.Error // 返回 error
- result.RowsAffected // 返回插入记录的条数
将切片数据传递给 Create
方法,GORM 将生成一个单一的 SQL 语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
- var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
- DB.Create(&users)
- for _, user := range users {
- user.ID // 1,2,3
- }
GORM 支持根据 map[string]interface{}
和 []map[string]interface{}{}
创建记录
- DB.Model(&User{}).Create(map[string]interface{}{
- "Name": "jinzhu", "Age": 18,
- })
- // 根据 `[]map[string]interface{}{}` 批量插入
- DB.Model(&User{}).Create([]map[string]interface{}{
- {"Name": "jinzhu_1", "Age": 18},
- {"Name": "jinzhu_2", "Age": 20},
- })
GORM 提供了 First
、Take
、Last
方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1
条件,且没有找到记录时,它会返回 ErrRecordNotFound
错误
- // 获取第一条记录(主键升序)
- db.First(&user)
- // SELECT * FROM users ORDER BY id LIMIT 1;
- // 获取一条记录,没有指定排序字段
- db.Take(&user)
- // SELECT * FROM users LIMIT 1;
- // 获取最后一条记录(主键降序)
- db.Last(&user)
- // SELECT * FROM users ORDER BY id DESC LIMIT 1;
- result := db.First(&user)
- result.RowsAffected // 返回找到的记录数
- result.Error // returns error
- // 检查 ErrRecordNotFound 错误
- errors.Is(result.Error, gorm.ErrRecordNotFound)
First
、Last
方法会根据主键查找到第一个、最后一个记录, 它仅在通过 struct 或提供 model 值进行查询时才起作用。如果 model 类型没有定义主键,则按第一个字段排序
- var user User
- // 可以
- DB.First(&user)
- // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
- // 可以
- result := map[string]interface{}{}
- DB.Model(&User{}).First(&result)
- // SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
- // 不行
- result := map[string]interface{}{}
- DB.Table("users").First(&result)
- // 但可以配合 Take 使用
- result := map[string]interface{}{}
- DB.Table("users").Take(&result)
- // 根据第一个字段排序
- type Language struct {
- Code string
- Name string
- }
- DB.First(&Language{})
- // SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
- // 获取全部记录
- result := db.Find(&users)
- // SELECT * FROM users;
- result.RowsAffected // 返回找到的记录数,相当于 `len(users)`
- result.Error // returns error
String条件
- // 获取第一条匹配的记录
- db.Where("name = ?", "jinzhu").First(&user)
- // SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
- // 获取全部匹配的记录
- db.Where("name <> ?", "jinzhu").Find(&users)
- // SELECT * FROM users WHERE name <> 'jinzhu';
- // IN
- db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
- // SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
- // LIKE
- db.Where("name LIKE ?", "%jin%").Find(&users)
- // SELECT * FROM users WHERE name LIKE '%jin%';
- // AND
- db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
- // SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
- // Time
- db.Where("updated_at > ?", lastWeek).Find(&users)
- // SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
- // BETWEEN
- db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
- // SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
Struct & Map 条件
- // Struct
- db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
- // SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
- // Map
- db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
- // SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
- // 主键切片条件
- db.Where([]int64{20, 21, 22}).Find(&users)
- // SELECT * FROM users WHERE id IN (20, 21, 22);
注意 当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果您的字段值为 0
、''
、false
或其他 零值,该字段不会被用于构建查询条件
Not 条件
构建NOT条件,用法与 Where
类似
- db.Not("name = ?", "jinzhu").First(&user)
- // SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
- // Not In
- db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
- // SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
- // Struct
- db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
- // SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
- // 不在主键切片中的记录
- db.Not([]int64{1,2,3}).First(&user)
- // SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
Or条件
- db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
- // SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
- // Struct
- db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
- // SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
- // Map
- db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
- // SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
选择想从数据库中检索的字段,默认情况下会选择全部字段
- db.Select("name", "age").Find(&users)
- // SELECT name, age FROM users;
- db.Select([]string{"name", "age"}).Find(&users)
- // SELECT name, age FROM users;
- db.Table("users").Select("COALESCE(age,?)", 42).Rows()
- // SELECT COALESCE(age,'42') FROM users;
指定从数据库检索记录时的排序方式
- db.Order("age desc, name").Find(&users)
- // SELECT * FROM users ORDER BY age desc, name;
- // Multiple orders
- db.Order("age desc").Order("name").Find(&users)
- // SELECT * FROM users ORDER BY age desc, name;
Limit
指定获取记录的最大数量 Offset
指定在开始返回记录之前要跳过的记录数量
- db.Limit(3).Find(&users)
- // SELECT * FROM users LIMIT 3;
- // 通过 -1 消除 Limit 条件
- db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
- // SELECT * FROM users LIMIT 10; (users1)
- // SELECT * FROM users; (users2)
- db.Offset(3).Find(&users)
- // SELECT * FROM users OFFSET 3;
- db.Limit(10).Offset(5).Find(&users)
- // SELECT * FROM users OFFSET 5 LIMIT 10;
- // 通过 -1 消除 Offset 条件
- db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
- // SELECT * FROM users OFFSET 10; (users1)
- // SELECT * FROM users; (users2)
- type result struct {
- Date time.Time
- Total int
- }
- db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
- // SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`
- db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
- // SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
- rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
- for rows.Next() {
- ...
- }
- rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
- for rows.Next() {
- ...
- }
- type Result struct {
- Date time.Time
- Total int64
- }
- db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
从模型中选择不相同的值
- db.Distinct("name", "age").Order("name, age desc").Find(&results)
GORM 允许通过 Select 方法选择特定的字段,如果您在应用程序中经常使用此功能,你也可以定义一个较小的结构体,以实现调用 API 时自动选择特定的字段
- 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
子查询可以嵌套在查询中,GORM 允许在使用 *gorm.DB
对象作为参数时生成子查询
- db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
- // SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
- subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
- db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)
- // SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
GORM 允许您在 Table
方法中通过 FROM 子句使用子查询
- db.Table("(?) as u", db.Model(&User{}).Select("name", "age")).Where("age = ?", 18).Find(&User{})
- // SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18
- subQuery1 := db.Model(&User{}).Select("name")
- subQuery2 := db.Model(&Pet{}).Select("name")
- db.Table("(?) as u, (?) as p", subQuery1, subQuery2).Find(&User{})
- // SELECT * FROM (SELECT `name` FROM `users`) as u, (SELECT `name` FROM `pets`) as p
使用 Group 条件可以更轻松的编写复杂 SQL
- db.Where(
- db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")),
- ).Or(
- db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"),
- ).Find(&Pizza{}).Statement
- // SELECT * FROM `pizzas` WHERE (pizza = "pepperoni" AND (size = "small" OR size = "medium")) OR (pizza = "hawaiian" AND size = "xlarge")
Save
会保存所有的字段,即使字段是零值
- 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;
当使用 Update
更新单列时,需要有一些条件,否则将会引起错误 ErrMissingWhereClause
。当使用 Model
方法,并且值中有主键值时,主键将会被用于构建条件
- // 条件更新
- 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;
- // User 的 ID 是 `111`
- db.Model(&user).Update("name", "hello")
- // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
- // 根据条件和 model 的值进行更新
- db.Model(&user).Where("active = ?", true).Update("name", "hello")
- // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
Updates
方法支持 struct
和 map[string]interface{}
参数。当使用 struct
更新时,默认情况下,GORM 只会更新非零值的字段
- // 根据 `struct` 更新属性,只会更新非零值的字段
- db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
- // UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
- // 根据 `map` 更新属性
- db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
- // UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
如果想要在更新时选定、忽略某些字段,您可以使用 Select
、Omit
- // Select with Map
- // User's ID is `111`:
- db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
- // UPDATE users SET name='hello' WHERE id=111;
- db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
- // UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
- // Select with Struct (select zero value fields)
- db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
- // UPDATE users SET name='new_name', age=0 WHERE id=111;
- // Select all fields (select all fields include zero value fields)
- db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
- // Select all fields but omit Role (select all fields include zero value fields)
- db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
如果尚未通过 Model
指定记录的主键,则 GORM 会执行批量更新
- // 根据 struct 更新
- db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
- // UPDATE users SET name='hello', age=18 WHERE role = 'admin';
- // 根据 map 更新
- db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
- // UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
- // 通过 `RowsAffected` 得到更新的记录数
- result := db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
- // UPDATE users SET name='hello', age=18 WHERE role = 'admin';
- result.RowsAffected // 更新的记录数
- result.Error // 更新的错误
删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete
- // Email 的 ID 是 `10`
- db.Delete(&email)
- // DELETE from emails where id = 10;
- // 带额外条件的删除
- db.Where("name = ?", "jinzhu").Delete(&email)
- // DELETE from emails where id = 10 AND name = "jinzhu";
GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字(如以下例子。也可以使用字符串——译者注)
- 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);
如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录
- 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%";
- // 返回所有列
- var users []User
- DB.Clauses(clause.Returning{}).Where("role = ?", "admin").Delete(&users)
- // DELETE FROM `users` WHERE role = "admin" RETURNING *
- // users => []User{{ID: 1, Name: "jinzhu", Role: "admin", Salary: 100}, {ID: 2, Name: "jinzhu.2", Role: "admin", Salary: 1000}}
- // 返回指定的列
- DB.Clauses(clause.Returning{Columns: []clause.Column{{Name: "name"}, {Name: "salary"}}}).Where("role = ?", "admin").Delete(&users)
- // DELETE FROM `users` WHERE role = "admin" RETURNING `name`, `salary`
- // users => []User{{ID: 0, Name: "jinzhu", Role: "", Salary: 100}, {ID: 0, Name: "jinzhu.2", Role: "", Salary: 1000}}
如果模型包含了一个 gorm.deletedat
字段(gorm.Model
已经包含了该字段),它将自动获得软删除的能力!
拥有软删除能力的模型调用 Delete
时,记录不会从数据库中被真正删除。但 GORM 会将 DeletedAt
置为当前时间, 并且你不能再通过普通的查询方法找到该记录。
- // user 的 ID 是 `111`
- db.Delete(&user)
- // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
- // 批量删除
- db.Where("age = ?", 20).Delete(&User{})
- // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
- // 在查询时会忽略被软删除的记录
- db.Where("age = 20").Find(&user)
- // SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
查询被软删除的记录
可以使用 Unscoped
找到被软删除的记录
- db.Unscoped().Where("age = 20").Find(&users)
- // SELECT * FROM users WHERE age = 20;
为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,可以在初始化时禁用它,这将获得大约 30%+ 性能提升。
- // 全局禁用
- db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
- SkipDefaultTransaction: true,
- })
- // 持续会话模式
- tx := db.Session(&Session{SkipDefaultTransaction: true})
- tx.First(&user, 1)
- tx.Find(&users)
- tx.Model(&user).Update("Age", 18)
要在事务中执行一系列操作,一般流程如下
- db.Transaction(func(tx *gorm.DB) error {
- // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
- if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
- // 返回任何错误都会回滚事务
- return err
- }
- if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
- return err
- }
- // 返回 nil 提交事务
- return nil
- })
GORM 支持嵌套事务,您可以回滚较大事务内执行的一部分操作
- db.Transaction(func(tx *gorm.DB) error {
- tx.Create(&user1)
- tx.Transaction(func(tx2 *gorm.DB) error {
- tx2.Create(&user2)
- return errors.New("rollback user2") // Rollback user2
- })
- tx.Transaction(func(tx2 *gorm.DB) error {
- tx2.Create(&user3)
- return nil
- })
- return nil
- })
Gorm 支持直接调用事务控制方法(commit、rollback)
- // 开始事务
- tx := db.Begin()
- // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
- tx.Create(...)
- // ...
- // 遇到错误时回滚事务
- tx.Rollback()
- // 否则,提交事务
- tx.Commit()
使用PrepareStmt
缓存预编译语句可以提高后续调用的速度,提高大约35%左右。
- db , err := gorm.Open(mysql.Open("username:password@tcp(localhost:9910)/database?charset=utf8"),&gorm.Config{
- PrepareStmt: true}
Kitex目前对Windows的支持不完善,建议使用虚拟机或WSL2
安装代码生成工具
- go install github.com/cloudwego/tool/cmd/kitex@latest
- go install github.com/cloudwego/thriftgo@latest
kitex
是 Kitex 框架提供的用于生成代码的一个命令行工具。目前,kitex 支持 thrift 和 protobuf 的 IDL,并支持生成一个服务端项目的骨架。
IDL是什么:IDL 全称是 Interface Definition Language,接口定义语言
为什么使用IDL:要进行 RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的,就好比两个人之间交流,需要保证在说的是同一个语言、同一件事。 这时候,就需要通过 IDL 来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道函数签名一样。
首先我们需要编写一个 IDL,这里以 thrift IDL 为例。
首先创建一个名为 echo.thrift
的 thrift IDL 文件。
然后在里面定义我们的服务
- namespace go api
- struct Request {
- 1: string message
- }
- struct Response {
- 1: string message
- }
- service Echo {
- Response echo(1: Request req)
- }
有了 IDL 以后我们便可以通过 kitex 工具生成项目代码了,执行如下命令:
example
为该服务的名字。最后一个参数则为该服务的 IDL 文件。生成后的项目结构如下:
.
|-- build.sh |-- echo.thrift |-- handler.go |-- kitex_gen | `-- api| |-- echo
| | |-- client.go
| | |-- echo.go
| | |-- invoker.go
| | `-- server.go | |-- echo.go | `-- k-echo.go|-- main.go
`-- script |-- bootstrap.sh `-- settings.py编写echo服务逻辑
需要编写的服务端逻辑都在
handler.go
这个文件中package main
import (
"context"
"example/kitex_gen/api"
)
// EchoImpl implements the last service interface defined in the IDL.
type EchoImpl struct{}
// Echo implements the EchoImpl interface.
func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
// TODO: Your code here...
return
}
这里的
Echo
函数就对应了我们之前在 IDL 中定义的echo
方法。现在让我们修改一下服务端逻辑,让
Echo
服务起到作用。func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
return &api.Response{Message: req.Message}, nil
}
编译运行
kitex 工具已经帮我们生成好了编译和运行所需的脚本:
编译:
client
:import "example/kitex_gen/api/echo"
import "github.com/cloudwego/kitex/client"
...
c, err := echo.NewClient("example", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
log.Fatal(err)
}
上述代码中,
echo.NewClient
用于创建client
,其第一个参数为调用的 服务名(用于微服务中的服务发现),第二个参数为 options,用于传入参数, 此处的client.WithHostPorts
用于指定服务端的地址。发起调用
import "example/kitex_gen/api"
...
req := &api.Request{Message: "my request"}
resp, err := c.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
if err != nil {
log.Fatal(err)
}
log.Println(resp)
上述代码中,我们首先创建了一个请求
req
, 然后通过c.Echo
发起了调用。其第一个参数为
context.Context
,通过通常用其传递信息或者控制本次调用的一些行为,你可以在后续章节中找到如何使用它。其第二个参数为本次调用的请求。
其第三个参数为本次调用的
options
,Kitex 提供了一种callopt
机制,顾名思义——调用参数 ,有别于创建 client 时传入的参数,这里传入的参数仅对此次生效。 此处的callopt.WithRPCTimeout
用于指定此次调用的超时(通常不需要指定,此处仅作演示之用)。在编写完一个简单的客户端后,我们终于可以发起调用了。
可以通过下述命令来完成这一步骤:
$GOPATH/src
下创建额外目录,进入该目录后再获取代码:$ mkdir -p $(go env GOPATH)/src/github.com/cloudwego
$ cd $(go env GOPATH)/src/github.com/cloudwego
若将代码放置于 GOPATH 之外,可直接获取
在当前目录下创建 hertz_demo 文件夹,进入该目录中
创建 main.go
文件
在 main.go
文件中添加以下代码
- 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()
- h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
- ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
- })
- h.Spin()
- }
生成go.mod
文件
.Next
,如图中的中间件 C。实现一个中间件
// 方式一
func MyMiddleware() app.HandlerFunc { return func(ctx context.Context, c *app.RequestContext) { // pre-handle // ... c.Next(ctx) }}
// 方式二
func MyMiddleware() app.HandlerFunc { return func(ctx context.Context, c *app.RequestContext) { c.Next(ctx) // call the next middleware(handler) // post-handle // ... }}
中间件会按定义的先后顺序依次执行,如果想快速终止中间件调用,可以使用以下方法,注意当前中间件仍将执行。
以上就是Go框架三件套Gorm、Kitex、Hertz怎么使用的详细内容,更多关于Go框架三件套Gorm、Kitex、Hertz怎么使用的资料请关注九品源码其它相关文章!