[ 项目记录 0 | 青训营笔记 ]
前言:
这次冬令营教我们使用的是 kitex + hertz 虽然平常还是 gin + grpc 用的比较多,但是借此机会还是尝试一下新的框架
但是由于 (我太菜了) kitex 和 hertz 的文档写的不是很详细,这边还是遇到了很多问题记录一下。
hertz 比较好弄配置什么的就按文档走就ok
p.s. 这已经算是踩坑合集了没想到写完的时候已经两天了
配置WSL2
(我感觉我弄复杂了,让我的电脑再次负重但是能用先用)
- 用管理员权限打开powershell,输入以打开虚拟环境
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
-
进入 Microsoft Store 下载 Ubuntu
-
喝茶等一会下载
-
有可能启动ubuntu报告
WslRegisterDistribution failed with error: 0x800701bc是因为没有升级linux内核使用wsl --update下载更新即可 -
打开ubuntu出现
Error code: Wsl/Service/0x8007273d,以管理员身份启动powershell , 运行netsh winsock reset -
在 powershell 里输入
wsl --set-default-version 2使用wsl2
ubuntu 配置 go
-
下载安装包
sudo wget https://golang.google.cn/dl/go1.19.linux-amd64.tar.gz -
解压
sudo tar -zxvf go1.19.linux-amd64.tar.gz -
环境配置 打开
vim ~/.bash_profile后 按i输入export GOPATH=/home/linxx/go export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin:$GOPATH/bin export GOPROXY=https://goproxy.cnesc+:wq保存退出
source .bash_profile使环境变量生效
-
下载
kitex + thriftgogo install github.com/cloudwego/kitex/tool/cmd/kitex@latest go install github.com/cloudwego/thriftgo@latest
docker-compose
这里另外补充一个docker-compose
-
安装docker desktop
-
如果你会发现 docker is starting... 或者运行
docker pserror during connect: This error may indicate that the docker daemon is not running.: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/containers/json": open //./pipe/docker_engine: The system cannot find the file specified. -
管理员 powershell 下运行
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart -
如果是
Error response from daemon: open \.\pipe\docker_engine_windows: The system cannot find the file specified. -
右键小鲸鱼
switch to linux containers -
此外这里说明一下确实是不需要 Hyper-v 的
-
进去之后在设置的
Docker Engine中配置镜像{ "registry-mirrors": [ "http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn" ], "insecure-registries": [], "debug": true, "experimental": false } -
docker desktop 里自带 docker compose 运行
docker compose up就可以部署容器辣
echo demo
wait,先不急写项目先实现一下文档的demo
创建 idl 文件,定义服务
namespace go api
struct Request {
1: string message
}
struct Response {
1: string message
}
service Echo {
Response echo(1: Request req)
}
在ubuntu里输入 kitex -module "awesome" -thrift frugal_tag -service helloserver ../idl/hello.thrift 这里 awesome 是 go mod 里 module的名字
tree /f 可以输出项目的结构
│ build.sh // 构建脚本
│ kitex.yaml
│ main.go // 程序入口
│ handler.go // 实现方法
│
├─kitex_gen // 生成的sever/client代码别动就对了
│ └─api
│ │ hello.go
│ │ k-consts.go
│ │ k-hello.go
│ │
│ └─hello
│ client.go
│ server.go
│ invoker.go
│ hello.go
│
└─script
bootstrap.sh
在 handler 编写好方法后,运行服务端
kitex 工具已经帮我们生成好了编译和运行所需的脚本:
$ sh build.sh
执行上述命令后,会生成一个 output 目录,里面含有我们的编译产物。运行:
$ sh output/bootstrap.sh
创建一个客户端
func main() {
// 创建
c, err := echo.NewClient("awesome", 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(1*time.Second))
if err != nil {
log.Fatal(err)
}
log.Println(resp)
}
这里是文档的解释:
echo.NewClient 用于创建 client,其第一个参数为调用的 服务名,第二个参数为 options,用于传入参数, 此处的 client.WithHostPorts 用于指定服务端的地址
上述代码中,我们首先创建了一个请求 req , 然后通过 c.Echo 发起了调用。
其第一个参数为 context.Context,通过通常用其传递信息或者控制本次调用的一些行为,你可以在后续章节中找到如何使用它。
其第二个参数为本次调用的请求。
其第三个参数为本次调用的 options ,Kitex 提供了一种 callopt 机制,顾名思义——调用参数 ,有别于创建 client 时传入的参数,这里传入的参数仅对此次生效。 此处的 callopt.WithRPCTimeout 用于指定此次调用的超时。
在同一系统下 $ go run main.go 就可以看到返回值了
*gorm gen
安装 go get -u gorm.io/gen
使用Gen提供的gen.NewGenerator函数就可以生成对应的model gorm 自动生成 参考资料二
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gen"
"gorm.io/gorm"
"strings"
"testing"
)
const MysqlConfig = "root:123456@(localhost:3306)/gen_test?charset=utf8mb4&parseTime=True&loc=Local"
func TestGEN(t *testing.T) {
// 连接数据库
db, err := gorm.Open(mysql.Open(MysqlConfig))
if err != nil {
panic(fmt.Errorf("cannot establish db connection: %w", err))
}
// 生成实例
g := gen.NewGenerator(gen.Config{
// 相对执行`go run`时的路径, 会自动创建目录
OutPath: "./query",
// WithDefaultQuery 生成默认查询结构体(作为全局变量使用), 即`Q`结构体和其字段(各表模型)
// WithoutContext 生成没有context调用限制的代码供查询
// WithQueryInterface 生成interface形式的查询代码(可导出), 如`Where()`方法返回的就是一个可导出的接口类型
Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
// 表字段可为 null 值时, 对应结体字段使用指针类型
FieldNullable: true, // generate pointer when field is nullable
// 表字段默认值与模型结构体字段零值不一致的字段, 在插入数据时需要赋值该字段值为零值的, 结构体字段须是指针类型才能成功, 即`FieldCoverable:true`配置下生成的结构体字段.
// 因为在插入时遇到字段为零值的会被GORM赋予默认值. 如字段`age`表默认值为10, 即使你显式设置为0最后也会被GORM设为10提交.
// 如果该字段没有上面提到的插入时赋零值的特殊需要, 则字段为非指针类型使用起来会比较方便.
FieldCoverable: false, // generate pointer when field has default value, to fix problem zero value cannot be assign: https://gorm.io/docs/create.html#Default-Values
// 模型结构体字段的数字类型的符号表示是否与表字段的一致, `false`指示都用有符号类型
FieldSignable: false, // detect integer field's unsigned type, adjust generated data type
// 生成 gorm 标签的字段索引属性
FieldWithIndexTag: false, // generate with gorm index tag
// 生成 gorm 标签的字段类型属性
FieldWithTypeTag: true, // generate with gorm column type tag
})
// 设置目标 db
g.UseDB(db)
// 自定义字段的数据类型
// 统一数字类型为int64,兼容protobuf
dataMap := map[string]func(detailType string) (dataType string){
"tinyint": func(detailType string) (dataType string) { return "int64" },
"smallint": func(detailType string) (dataType string) { return "int64" },
"mediumint": func(detailType string) (dataType string) { return "int64" },
"bigint": func(detailType string) (dataType string) { return "int64" },
"int": func(detailType string) (dataType string) { return "int64" },
}
// 要先于`ApplyBasic`执行
g.WithDataTypeMap(dataMap)
// 自定义模型结体字段的标签
// 将特定字段名的 json 标签加上`string`属性,即 MarshalJSON 时该字段由数字类型转成字符串类型
jsonField := gen.FieldJSONTagWithNS(func(columnName string) (tagContent string) {
toStringField := `balance, `
if strings.Contains(toStringField, columnName) {
return columnName + ",string"
}
return columnName
})
// 将非默认字段名的字段定义为自动时间戳和软删除字段;
// 自动时间戳默认字段名为:`updated_at`、`created_at, 表字段数据类型为: INT 或 DATETIME
// 软删除默认字段名为:`deleted_at`, 表字段数据类型为: DATETIME
autoUpdateTimeField := gen.FieldGORMTag("update_time", "column:update_time;type:int unsigned;autoUpdateTime")
autoCreateTimeField := gen.FieldGORMTag("create_time", "column:create_time;type:int unsigned;autoCreateTime")
softDeleteField := gen.FieldType("delete_time", "soft_delete.DeletedAt")
// 模型自定义选项组
fieldOpts := []gen.ModelOpt{jsonField, autoCreateTimeField, autoUpdateTimeField, softDeleteField}
// 创建模型的结构体,生成文件在 model 目录; 先创建的结果会被后面创建的覆盖
// 这里创建个别模型仅仅是为了拿到`*generate.QueryStructMeta`类型对象用于后面的模型关联操作中
User := g.GenerateModel("user")
// 创建全部模型文件, 并覆盖前面创建的同名模型
allModel := g.GenerateAllTable(fieldOpts...)
// 创建有关联关系的模型文件
// 可以用于指定外键
//Score := g.GenerateModel("score",
// append(
// fieldOpts,
// // user 一对多 address 关联, 外键`uid`在 address 表中
// gen.FieldRelate(field.HasMany, "user", User, &field.RelateConfig{GORMTag: "foreignKey:UID"}),
// )...,
//)
// 创建模型的方法,生成文件在 query 目录; 先创建结果不会被后创建的覆盖
g.ApplyBasic(User)
g.ApplyBasic(allModel...)
g.Execute()
}
生成了两个文件夹,其中model文件夹下面是数据表对应的结构体,query文件夹下面则是生成的查询函数。
好了,demo可以实现了,但是还是先不开始写项目,先研究一下范例 easy-note
先运行起来
大概梳理一下运行的逻辑,ok可以写项目了