本小文用于记录如何编写一个完整的rpc通讯的client和service部分,不包括前端的调用
首先明确我们需要什么,依据rpc的基本通讯流程,我首先需要client和service的代码,利用cwgo(kitex)的代码生成能力分别生成双端的代码,需要注意的点有两个,1.服务端和客户端共用一个idl作为依赖2.服务端的代码不再生成客户端代码,将客户端代码统一管理(从代码角度就是要pass -use)
完成这些就有了一个基础的rpc框架
在开发前,要注意一个问题,在 go mod 开发模式下,相对路径的导包方式不支持。会报错误: local import "./package1" in non-local package
解决方案
在 go.mod 文件中使用 replace 语法,将要导入的包替换为本地的相对地址
github.com/hcdog/pycode/gomall/rpc_gen => ../../rpc_gen
2.2.1 定义用户功能的数据模型
数据模型:数据库系统的核心组成部分,它提供了一种抽象的方式来描述和组织数据,以及这些数据之间的关系。数据模型定义了数据的结构、类型、约束条件以及数据之间的操作。它是数据库设计和实现的基础,为数据库系统的构建、使用和维护提供了指导。
建立一个结构体,为后续的代码提供可传递的变量,这是client代码和service代码生成完后第一个要做的事
这里涉及到一个关键的东西,标签tag
标签(Tag)通常指的是结构体字段后面跟着的反引号(``)内的字符串。这些标签为结构体字段提供了元数据,可以被用于多种目的,如文档说明,但更关键的是用于JSON序列化/反序列化、数据库ORM映射、验证等。
1.数据库ORM映射:
结构体关联数据库,正常操作数据库是使用mysql语句,但是为了简化这一流程,我们使用结构体映射到数据库,这样开发起来就会比较简单,这也是我们的主要用法,依据查到的资料,常见的gorm标签有:
1. `gorm:"primary_key"`: 定义字段作为模型的主键。
2. `gorm:"column:<column_name>"`: 指定字段在数据库表中的列名。
3. `gorm:"type:<data_type>"`: 指定字段的数据库数据类型。
4. `gorm:"unique"`: 定义字段的值在数据库中是唯一的。
5. `gorm:"not null"`: 定义字段在数据库中不允许为空。
6. `gorm:"default:<default_value>"`: 指定字段的默认值。
7. `gorm:"size:"`: 指定字段的大小,通常用于字符串类型字段。
8. `gorm:"autoIncrement"`: 定义字段自增。
9. `gorm:"index"`: 在数据库中为字段创建索引。
10. `gorm:"uniqueIndex"`: 在数据库中为字段创建唯一索引。
11. `gorm:"primaryKey"`: 定义字段为数据库表的主键。
12. `gorm:"default:"`: 指定字段的默认值。
13. `gorm:"-"`: 定义字段在数据库模型中不可见。
14. `gorm:"type:<data_type>;size:"`: 结合使用 `type` 和 `size` 标签,可以同时指定字段的数据类型和大小。
15. `gorm:"association_foreignkey:<column_name>"`: 定义关联表中外键的列名。
16. `gorm:"many2many:<join_table_name>"`: 定义多对多关联关系,并指定连接表的表名。
17. `gorm:"->;"`: 指定外键约束,例如 `ONDELETE:CASCADE`。
比如我们这里编写一个商品的数据模型,设置一个标签
type Base struct {ID int gorm:"primarykey"CreatedAt time.TimeUpdatedAt time.Time}
这里设置的标签就是这个id是int类型,且是主键,作为唯一标识
2.JSON序列化/反序列化:
字符串转化为go对象是反序列化,反之是序列化
比如这个结构体
type Product struct {
Base
Name string json:"name"
Description string json:"description"
Picture string json:"picture"
Price float32 json:"price"
Categories []Category json:"categories" gorm:"many2many:product_category"
}
这里就是说 Name 变量会被命名为name,这意味着当Product结构体被序列化为JSON时,Name字段将出现在JSON对象中作为name键的值;同样地,当从JSON反序列化回Product结构体时,JSON对象中的name键的值将被赋给Name字段。
自定义表名称
别忘了写一个自定义表名称函数,不然创建的表都是结构体的名字
func (p Product) TableName() string {
return "product"
}
后续有数据处理方面方法再写在这里,暂时结束,可以开始编写服务代码
2.2.2改造数据库初始化
为刚才建立的数据模型,配置mysql初始化函数,在主函数中完成初始化
2.2.3编写服务函数
主要在service文件夹下的各个服务文件
逻辑很简单,想清楚需要什么,比如,我要让注册时候密码两次输入一致,那就写
if req.Password != req.ConfirmPassword {err = errors.New("Password must be the same as ConfirmPassword")return}
2.2.4服务注册
服务编写完后,在主函数中完成服务注册
r, err := consul.NewConsulRegister("127.0.0.1:8500")
if err != nil {
log.Fatal(err)
}
opts = append(opts, server.WithRegistry(r))
注册完后,你可以在localhost:8500(即你设定的consul端口)查看服务情况。若注册不成功,注意更改配置文件中的address为本地ip+服务端口号"172.30.0.1:8888"
以上就完成了一个服务的后端部分的程序编写,后续再在前端代码中写相关接口和调用