这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
Part1 Kitex
RPC 框架 Kitex
首先让我们先聊一下什么是rpc
我们说的rpc是指角色扮演游戏(Role-playing character)- 远程过程调用(Remote Procedure Call)
所谓远程调用过程也叫远端程序呼叫 是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统 常用于分布式计算中
远程过程调用是一个分布式计算的客户端-服务器(Client/Server)的例子,它简单而又广受欢迎。远程过程调用总是由客户端对服务器发出一个执行若干过程请求,并用客户端提供的参数。执行结果将返回给客户端。由于存在各式各样的变体和细节差异,对应地衍生了各式远程过程调用协议,而且它们并不互相兼容。
但互不兼容可不是好事 为了允许不同的客户端均能访问服务器,许多标准化的 RPC 系统应运而生了。其中大部分采用接口描述语言(Interface Description Language,IDL),方便跨平台的远程过程调用
简单来说
rpc 是以客户端服务端的形式 通过发送请求-接受回应进行信息交互的系统
idl 是为了允许不同的客户端均能访问服务器 产生的标准化接口描述语言
我们继续聊kitex
Kitex 框架及命令行工具,默认支持 thrift 和 proto3 两种 IDL,对应的 Kitex 支持 thrift 和 protobuf 两种序列化协议。 传输上 Kitex 使用扩展的 thrift 作为底层的传输协议(注:thrift 既是 IDL 格式,同时也是序列化协议和传输协议)。
Kitex 自带了一个同名的命令行工具 kitex,用来帮助大家很方便地生成代码,新项目的生成以及之后我们会学到的 server、client 代码的生成都是通过 kitex 工具进行。
按照以下命令可生成基础的文件目录
$ kitex -module mod里的包名 -service 服务名称 idl文件
生成的目录如下
其中:
build.sh 是为了快速启动
echo.thrift 是我们的idl
handler.go 里面封装了我们的服务逻辑
kitex_gen 里面装的是我们的文件
main.go 是服务端的启动文件
script 是为了生成可执行文件
关于脚本:
kitex 工具已经帮我们生成好了编译和运行所需的脚本:
编译:
$ sh build.sh
执行上述命令后,会生成一个 output 目录,里面含有我们的编译产物。
运行:
$ sh output/bootstrap.sh
执行上述命令后,Echo 服务就开始运行啦
客户端:
import "example/kitex_gen/api"//导入包
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)
}
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)
NewClient:
上述代码中,echo.NewClient 用于创建 client,其第一个参数为调用的 服务名,第二个参数为 options,用于传入参数, 此处的 client.WithHostPorts 用于指定服务端的地址
上述代码中,我们首先创建了一个请求 req , 然后通过 c.Echo 发起了调用。
发送函数:
其第一个参数为 context.Context,通过通常用其传递信息或者控制本次调用的一些行为。
其第二个参数为本次调用的请求。
其第三个参数为本次调用的 options ,Kitex 提供了一种 callopt 机制,顾名思义——调用参数 ,有别于创建 client 时传入的参数,这里传入的参数仅对此次生效。
Part2 hertz
HTTP 框架 Hertz
Hertz 是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttp、gin、echo 的优势, 并结合字节跳动内部的需求,使其具有高易用性、高性能、高扩展性等特点,目前在字节跳动内部已广泛使用。 如今越来越多的微服务选择使用 Golang,如果对微服务性能有要求,又希望框架能够充分满足内部的可定制化需求,Hertz 会是一个不错的选择
- 生成代码
hz new go mod tidy来整理和更新依赖go build -o hertz_demo来构建文件- 启动后就可以通过curl来进行测试
Part3 gorm
db: 数据库 Golang写的,开发人员友好的ORM库
-
连接数据库
导入依赖文件。。。 //配置MySQL连接参数 username := "root" //账号 password := "123456" //密码 host := "127.0.0.1" //数据库地址,可以是Ip或者域名 port := 3306 //数据库端口 Dbname := "tizi365" //数据库名 //通过前面的数据库参数,拼接MYSQL DSN, 其实就是数据库连接串(数据源名称) //MYSQL dsn格式: {username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local //类似{username}使用花括号包着的名字都是需要替换的参数 dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname) //连接MYSQL db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) -
用结构体定义你的表模型
-
增
u := User{ Username:"tizi365", Password:"123456", CreateTime:time.Now().Unix(), } db.create(&u) -
删
利用where语句: db.Where("type = ?", 5).Delete(&Food{}) 传入空的结构体即可
-
改
food定义其中id为某一个值 之后 db.Model(&food).Update("price", 25) 把这个food的price更新为25 利用where语句进行修改 db.Model(&Food{}).Where("create_time > ?", "2018-11-06 20:00:00").Update("price", 25) Updatas //通过结构体变量设置更新字段 updataFood := Food{ Price:120, Title:"柠檬雪碧", } //根据food模型更新数据库记录 //Updates会忽略掉updataFood结构体变量的零值字段, 所以生成的sql语句只有price和title字段。 db.Model(&food).Updates(&updataFood) //设置Where条件,Model参数绑定一个空的模型变量 db.Model(&Food{}).Where("price > ?", 10).Updates(&updataFood) -
查
type Food struct { Id int Title string Price float32 Stock int Type int //mysql datetime, date类型字段,可以和golang time.Time类型绑定, 详细说明请参考:gorm连接数据库章节。 CreateTime time.Time }- Take
查询一条记录
例子: //定义接收查询结果的结构体变量 food := Food{} //等价于:SELECT * FROM `foods` LIMIT 1 db.Take(&food)- First
查询一条记录,根据主键ID排序(正序),返回第一条记录
例子: //等价于:SELECT * FROM `foods` ORDER BY `foods`.`id` ASC LIMIT 1 db.First(&food)- Last
查询一条记录, 根据主键ID排序(倒序),返回第一条记录
//等价于:SELECT * FROM `foods` ORDER BY `foods`.`id` DESC LIMIT 1 //语义上相当于返回最后一条记录 db.Last(&food)- Find
查询多条记录,Find函数返回的是一个数组
//因为Find返回的是数组,所以定义一个商品数组用来接收结果 var foods []Food //等价于:SELECT * FROM `foods` db.Find(&foods)where语句
db.Where("id = ?", 10).Take(&food)select选择返回内容
db.Select("id,title").Where("id = ?", 1).Take(&food)order语句
db.Where("create_time >= ?", "2018-11-06 00:00:00").Order("create_time desc").Find(&foods)分页和偏移
db.Order("create_time desc").Limit(10).Offset(0).Find(&foods)Count函数,直接返回查询匹配的行数。
db.Model(Food{}).Count(&total)分组
type Result struct { Type int Total int } var results []Result //等价于: SELECT type, count(*) as total FROM `foods` GROUP BY type HAVING (total > 0) db.Model(Food{}).Select("type, count(*) as total").Group("type").Having("total > 0").Scan(&results) //scan类似Find都是用于执行查询语句,然后把查询结果赋值给结构体变量,区别在于scan不会从传递进来的结构体变量提取表名. //这里因为我们重新定义了一个结构体用于保存结果,但是这个结构体并没有绑定foods表,所以这里只能使用scan查询函数。 - Take
提示:gorm库是协程安全的,gorm提供的函数可以并发的在多个协程安全的执行。
参考资料:
'https://www.tizi365.com/archives/20.html'
'https://jasperxu.github.io/gorm-zh/associations.html#bt'
'https://juejin.cn/post/7188225875211452476/#heading-16'
码风略丑 读者见谅 --2023/1/23