From 576fc83d5e71d9762f6244b5e319415e5b98d8cd Mon Sep 17 00:00:00 2001 From: oldme Date: Fri, 6 Dec 2024 16:43:05 +0800 Subject: [PATCH] proxima --- .../proxima-book/assets/architecture.png | Bin 0 -> 22825 bytes docs/course/proxima-book/proxima-book.md | 14 +- ...31\344\275\234\347\272\246\345\256\232.md" | 6 +- ...66\346\236\204\344\273\213\347\273\215.md" | 25 +++ ...71\347\233\256\344\273\213\347\273\215.md" | 38 ----- ...57\345\242\203\345\207\206\345\244\207.md" | 79 +++++----- ...56\345\210\235\345\247\213\345\214\226.md" | 26 +++- ...15\347\275\256\345\207\206\345\244\207.md" | 87 ++++------- ...32\345\212\241\351\200\273\350\276\221.md" | 34 +++++ ...15\345\212\241\345\207\206\345\244\207.md" | 44 ------ ...17\350\256\256\346\226\207\344\273\266.md" | 105 +++++++++++++ ...4.\346\216\247\345\210\266\345\231\250.md" | 67 ++++++++ ...57\345\212\250\350\277\220\350\241\214.md" | 144 ++++++++++++++++++ 13 files changed, 471 insertions(+), 198 deletions(-) create mode 100644 docs/course/proxima-book/assets/architecture.png create mode 100644 "docs/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/1.2.\346\236\266\346\236\204\344\273\213\347\273\215.md" delete mode 100644 "docs/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/1.2.\351\241\271\347\233\256\344\273\213\347\273\215.md" create mode 100644 "docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" delete mode 100644 "docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\345\276\256\346\234\215\345\212\241\345\207\206\345\244\207.md" create mode 100644 "docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" create mode 100644 "docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.4.\346\216\247\345\210\266\345\231\250.md" create mode 100644 "docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" diff --git a/docs/course/proxima-book/assets/architecture.png b/docs/course/proxima-book/assets/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..768f36554372adc9eb33338e850bd3553ec3d0ad GIT binary patch literal 22825 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 vQ1F5YUkHUP>g@mwfYOPE{@0@)5_h$c-Utw% 如果你的版本和我不一致,也无需担心,`Go`和`GoFrame`都有着强大的向下兼容性。 +## gRPC +--- +`gRPC`是一个由 `Google` 开发的远程过程调用(RPC)框架,基于`HTTP/2`。它使用 `Protocol` 作为默认的序列化格式。 + +`Go`语言通过`gRPC-go`插件提供`gRPC`功能。执行命令安装插件: +```bash +$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest +go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest +``` + +### gRPC测试工具 +`gRPC`接口开发完成后,需要一些测试工具,来检测它是否正常运行。比较流行的测试工具有`Postman`、`Apifox`、`Apipost`等,它们大同小异,你可以根据自己的喜好选择一个。 + +本书统一使用`json`展示测试结果,例如: +```json +grpc 127.0.0.1:32001.account.v1.Account.UserRegister +{ +    "username": "oldme", +    "password": "123456", +    "email": "tyyn1022@gmail.com" +} +{ +    "id": 1 +} +``` + +它们分别代表请求地址,请求参数,响应参数。 ## Protocol --- - `Protocol` 是 Google 设计的数据序列化格式,用于结构化数据的序列化和反序列化。使用 `.proto` 文件定义消息结构,然后通过编译器生成相应语言的代码。 + `Protocol` 是 `Google` 设计的数据序列化格式,用于结构化数据的序列化和反序列化。使用 `.proto` 文件定义消息结构,然后通过编译器生成相应语言的代码。 根据不同的操作系统,在 [Protocal Release](https://github.com/protocolbuffers/protobuf/releases) 下载对应的文件安装。如果是 `MacOS` 环境,可以使用 `brew` 工具安装依赖: @@ -33,17 +49,6 @@ $ brew install grpc protoc-gen-go protoc-gen-go-grpc $protoc --version libprotoc 26.1 ``` -版本只要不是相差太大,都无须担心。 - -## gRPC ---- -`gRPC`是一个由 Google 开发的远程过程调用(RPC)框架,基于HTTP/2。它使用 `Protocol` 作为默认的序列化格式。可以理解为 `HTTP` 和 `JSON` 的关系。 - -`Go`语言通过`gRPC-go`插件提供`gRPC`功能。执行命令安装插件: -```bash -$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest -go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest -``` ## etcd --- @@ -57,28 +62,15 @@ services: etcd: image: "bitnami/etcd:3.5" container_name: "etcd" - # 总是重启 restart: "always" ports: - 2379:2379 environment: - # 时区设置 - TZ=Asia/Shanghai # 允许无认证访问 - ALLOW_NONE_AUTHENTICATION=yes # etcd 客户端访问URL - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379 - volumes: - # 将数据映射到宿主机 - - etcd_data:/bitnami/etcd - -volumes: - etcd_data: - driver: local - driver_opts: - type: "none" - o: "bind" - device: "/home/docker/etcd/data" ``` 如果安装成功,在浏览器访问 [http://IP:2379/version](http://IP:2379/version),会出现以下信息: @@ -86,8 +78,15 @@ volumes: {"etcdserver": "3.5.17","etcdcluster": "3.5.0"} ``` -如果你更酷一些,安装个`etcd`集群或者学习一下`etcd`的基础使用,可以参考[此文](https://oldme.net/article/32)。 +如果你想更酷一些,安装个`etcd`集群或者学习一下`etcd`的基础使用,可以参考[此文](https://oldme.net/article/32)。 ## 数据库 --- -`MySQL`的安装不必多说,使用其他数据库亦可。 \ No newline at end of file +`MySQL`的安装不必多说,使用其他数据库亦可。 + +需要注意的是,微服务架构下,每个单独的服务都应当有自己的数据库。所以,我们需要建立两个数据库,名称分别是`user`和`word`。 + +```sql +CREATE DATABASE user; +CREATE DATABASE word; +``` \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/1.4.\351\241\271\347\233\256\345\210\235\345\247\213\345\214\226.md" "b/docs/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/1.4.\351\241\271\347\233\256\345\210\235\345\247\213\345\214\226.md" index 092cca3580a..229addde655 100644 --- "a/docs/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/1.4.\351\241\271\347\233\256\345\210\235\345\247\213\345\214\226.md" +++ "b/docs/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/1.4.\351\241\271\347\233\256\345\210\235\345\247\213\345\214\226.md" @@ -1,15 +1,24 @@ ## 初始化仓库 --- -执行命令,初始化一个名为`proxima`的微服务仓库: +执行命令,初始化一个名为`proxima`的`Monorepo`仓库: ```bash $ gf init proxima -m ``` +修改`go`语言的最低依赖版本,确保大于`GoFrame`最低版本要求即可。 + +*go.mod* +```text +module proxima + +go 1.23.4 +``` + `GoFrame`升级到最新版本: ```bash $ cd proxima -gf -up +gf up ``` 删除不必要的示例文件: @@ -27,16 +36,19 @@ utility go.mod go.sum ``` + 在`Monorepo`仓库模式下,根目录只提供对项目依赖管理,不存在`main.go`文件。`app`目录下保存各微服务的代码文件,例如`app/user/main.go`,`app/word/main.go`。 -## 建立数据库 +## 安装微服务组件 --- -微服务架构下,每个单独的服务都应当有自己的数据库。所以,我们需要建立两个数据库,名称分别是`user`和`word`。 +安装`grpcx`组件,让`GoFrame`支持微服务开发。 +```bash +$ go get -u github.com/gogf/gf/contrib/rpc/grpcx/v2 +``` ## 安装数据库驱动 --- -和单体服务一样,也需要安装对应的数据库驱动,这里演示的是`MySQL`: - +和单体服务一样,也需要安装对应的数据库驱动,这里演示的是`MySQL`。 ```bash -go get -u github.com/gogf/gf/contrib/drivers/mysql/v2 +$ go get -u github.com/gogf/gf/contrib/drivers/mysql/v2 ``` diff --git "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" index c3496e888a1..13b8915a748 100644 --- "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" +++ "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" @@ -1,4 +1,4 @@ -本章都是一些基础准备,和单体服务一模一样。 +微服务的大部分开发和单体服务都一致,甚至比单体服务简单许多。本章都是一些基础准备,想必各位都轻车熟路。 ## 代码初始化 --- @@ -13,51 +13,22 @@ you can now run "cd app/user && gf run main.go" to start your journey, enjoy! 成功后,会在`app`下建立一个微服务,它其实和单体服务没什么两样,就是缺少了`go.mod`和`go.sum`文件。 -把初始的示例文件全部删除,留下一个空白的环境,用作后续开发。删除如下目录内的所有文件: +将下列文件全部删除,留下一个空白的环境,用作后续开发。 ```text -api/* -internal/controller/* +app/user/api/* +app/user/internal/controller/* +app/user/internal/cmd/cmd.go ``` -编辑`cmd`文件,去掉不必要的代码。 - -*app/user/internal/cmd/cmd.go* -```go -package cmd - -import ( - "context" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/os/gcmd" -) - -var ( - Main = gcmd.Command{ - Name: "main", - Usage: "main", - Brief: "start http server", - Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { - s := g.Server() - s.Group("/", func(group *ghttp.RouterGroup) { - group.Middleware(ghttp.MiddlewareHandlerResponse) - }) - s.Run() - return nil - }, - } -) -``` - -代码完成后,我们就可以进入微服务仓库,和单体服务走一样的流程进行开发。 - +代码完成后,我们就可以进入微服务仓库,开始正式的开发。 ```bash $ cd app/user ``` + ## 生成数据模型 --- -在`user`下,执行`SQL`语句,建立保存用户数据的表: +### 建立数据表 +在`user`数据库下,执行`SQL`语句,建立保存用户数据的表: ```sql CREATE TABLE users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, @@ -69,8 +40,7 @@ CREATE TABLE users ( ); ``` -修改配置文件,生成数据模型,这里和单体服务一样。 - +### 生成dao模型 *app/user/hack/config.yaml* ```yaml gfcli: @@ -91,19 +61,26 @@ done! > 注意,应该在微服务仓库下执行`gf gen dao`命令,也就是`app/user`目录下,后续的其他相关操作开发也类似。 -## 引入数据库驱动 ---- -这里和单体服务一样,在`main.go`下引入。 - -*app/user/main.go* -```go -package main - -import ( - _ "github.com/gogf/gf/contrib/drivers/mysql/v2" -) +### 生成pbentity模型 +*app/user/hack/config.yaml* +```bash +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/user" + descriptionTag: true -func main() { - cmd.Main.Run(gctx.GetInitCtx()) -} -``` \ No newline at end of file + pbentity: + - link: "mysql:root:12345678@tcp(srv.com:3306)/user" +``` + +```bash +$ gf gen pbentity +generated: D:\project\proxima\app\user\manifest\protobuf\pbentity\users.proto +done! +``` + +### 与 `gen dao` 的差别 + +- `gen dao` 生成的数据文件是`go`文件,主要在微服务内部使用,例如`ORM`操作; +- `gen pbentity`生成的数据文件是`proto`文件,主要用作`gRPC`微服务之间的通讯。 diff --git "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" new file mode 100644 index 00000000000..f778d8d1b5b --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" @@ -0,0 +1,34 @@ +微服务的业务逻辑存放在`*/internal/logic`下,和单体业务一样。大家都是久经沙场的老将了,作者也不献丑,就简单做个样子。 + +*app/user/internal/logic/account/account.go* +```go +package account + +import ( + "context" + + "github.com/gogf/gf/v2/os/gtime" + "proxima/app/user/internal/dao" + "proxima/app/user/internal/model/entity" +) + +func Register(ctx context.Context) (id int, err error) { + return 1, nil +} + +func Login(ctx context.Context) (token string, err error) { + return "I am token", nil +} + +// Info get user info +func Info(ctx context.Context, token string) (user *entity.Users, err error) { + return &entity.Users{ + Id: 1, + Username: "oldme", + Password: "123456", + Email: "tyyn1022@gmail.com", + CreatedAt: gtime.New("2024-12-05 22:00:00"), + UpdatedAt: gtime.New("2024-12-05 22:00:00"), + }, nil +} +``` \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\345\276\256\346\234\215\345\212\241\345\207\206\345\244\207.md" "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\345\276\256\346\234\215\345\212\241\345\207\206\345\244\207.md" deleted file mode 100644 index 3fcec737234..00000000000 --- "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.2.\345\276\256\346\234\215\345\212\241\345\207\206\345\244\207.md" +++ /dev/null @@ -1,44 +0,0 @@ -本章是一些微服务准备,和单体服务不太一致。 - -## 生成数据模型 ---- -*app/user/hack/config.yaml* -```bash -gfcli: - gen: - dao: - - link: "mysql:root:12345678@tcp(srv.com:3306)/user" - descriptionTag: true - - pbentity: - - link: "mysql:root:12345678@tcp(srv.com:3306)/user" -``` - -```bash -$ gf gen pbentity -generated: D:\project\proxima\app\user\manifest\protobuf\pbentity\users.proto -done! -``` - -### 与 `gen dao` 的差别 - -- `gen dao` 生成的数据文件是`go`文件,主要在微服务内部使用,例如`ORM`操作; -- `gen pbentity`生成的数据文件是`proto`文件,主要用作`gRPC`微服务之间的通讯。 - -## 配置文件 ---- -和`HTTP`服务一样,`gRPC`微服务也需要有自己的配置文件。 - -*app/user/manifest/config/config.yaml* -```go -grpc: - name: "user" # 服务名称 - address: ":32001" # 自定义服务监听地址 - -database: - default: - link: "mysql:root:12345678@tcp(srv.com:3306)/user" - debug: true -``` - -`gprc`字段定义了两个字段,微服务名称和监听端口,微服务名称将用作服务发现。这两个是必要的,完整配置见[配置模板](https://goframe.org/docs/micro-service/config#%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF)。 \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" new file mode 100644 index 00000000000..4218e6880b9 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" @@ -0,0 +1,105 @@ +协议文件指的是`*.proto`文件,`proto`是`gRPC`协议通讯的标准,可以类比为`json`和`HTTP`。但是千万不要理解`proto`就是`json`,他们还是有一定区别的。`proto`同时定义“接口”信息和响应参数,请求参数。 + +`proto`文件统一存放在`manifest/protobuf`下,和普通的`HTTP`服务一样,使用目录层级来管理接口版本。 + +## 用户注册 +--- +创建一个名为`account`的目录,管理账户相关业务。 + +*app/user/manifest/protobuf/account/v1/account.proto* +```proto +// protoc --go_out=plugins=grpc:. *.proto + +syntax = "proto3"; + +package account.v1; + +option go_package = "proxima/app/user/api/account/v1"; + +service Account{ + rpc UserRegister(UserRegisterReq) returns (UserRegisterRes) {} +} + +message UserRegisterReq { + string username = 1; // v:required|min-length:2 + string password = 2; // v:required|min-length:6 + string email = 3; // v:required|email +} + +message UserRegisterRes { + int32 id = 1; +} +``` + +简单的介绍一下`proto`语法: +- **syntax** 规定本文件语法版本; +- **package** 定义的是服务命名空间,可以理解为包名; +- **option** 设定编译选项,`go_package`指定生成的`Go`代码所属的包名。*在`GoFrame`中固定格式是`项目名 + app + 微服务名称 + api + 模块名 + v1`*; +- **service** 定义远程调用方法,一般是`RPC`,规定其请求和响应参数; +- **message** 定义数据结构,`string`是数据类型,`username`是字段名,赋值号后面的递增数字是字段编号。*最后面的注释是框架提供的参数校检,使用方式普通的`HTTP`接口一致*。 + +我们的文件定义了以下内容: +- 使用`proto3`语法版本的定义。 +- 定义了包名为`account.v1`。 +- 设置了`Go`代码生成的包路径选项`go_package`为`proxima/app/user/api/account/v1`。 +- 定义了一个`Account`服务,包含一个`RPC`方法`UserRegister`,它接受`UserRegisterReq`消息并返回`UserRegisterRes`消息。 +- 定义了一个消息类型`UserRegisterReq`,包含三个字段: + - `username` (string类型,编号为1) + - `password` (string类型,编号为2) + - `email` (string类型,编号为3) +- 定义了一个消息类型`UserRegisterRes`,包含一个字段: + - `id` (int32类型,编号为1) + +## 用户登录/查询 +--- +照葫芦画瓢,我们继续定义用户登录和查询接口。最后的文件内容如下: + +*app/user/manifest/protobuf/account/v1/account.proto* +```proto +// protoc --go_out=plugins=grpc:. *.proto + +syntax = "proto3"; + +package account.v1; + +option go_package = "proxima/app/user/api/account/v1"; + +import "pbentity/users.proto"; + +service Account{ + rpc UserRegister(UserRegisterReq) returns (UserRegisterRes) {} + rpc UserLogin(UserLoginReq) returns (UserLoginRes) {} + rpc UserInfo(UserInfoReq) returns (UserInfoRes) {} +} + +message UserRegisterReq { + string username = 1; // v:required|min-length:2 + string password = 2; // v:required|min-length:6 + string email = 3; // v:required|email +} + +message UserRegisterRes { + int32 id = 1; +} + +message UserLoginReq { + string username = 1; // v:required|min-length:2 + string password = 2; // v:required|min-length:6 +} + +message UserLoginRes { + string token = 1; +} + +message UserInfoReq { + string token = 1; // v:required +} + +message UserInfoRes { + pbentity.Users user = 1; +} +``` + +这里多了两个新的语法: +- `import "pbentity/users.proto";`,代表引入了其他`proto`文件。引入的这个文件是`gf gen pbentity`生成的; +- `pbentity.Users user`调用引入的数据模型,和`go`的结构体几乎一致。 \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.4.\346\216\247\345\210\266\345\231\250.md" "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.4.\346\216\247\345\210\266\345\231\250.md" new file mode 100644 index 00000000000..1c4af228168 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.4.\346\216\247\345\210\266\345\231\250.md" @@ -0,0 +1,67 @@ +`HTTP`服务的控制器由`gf gen ctrl`生成,微服务由`gf gen pb`生成。 + +```bash +$ gf gen pb +``` + +`gen pb`命令需要各类依赖都正常。如果执行成功,会生成若干`go`文件。我们只需要关注控制器,其他由框架维护。后续的开发步骤,和`HTTP`服务一样——调用`logic`。 + +*app/user/internal/controller/account/account.go* +```go +package account + +import ( + "context" + + "google.golang.org/protobuf/types/known/timestamppb" + v1 "proxima/app/user/api/account/v1" + "proxima/app/user/api/pbentity" + "proxima/app/user/internal/logic/account" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" +) + +type Controller struct { + v1.UnimplementedAccountServer +} + +func Register(s *grpcx.GrpcServer) { + v1.RegisterAccountServer(s.Server, &Controller{}) +} + +func (*Controller) UserRegister(ctx context.Context, req *v1.UserRegisterReq) (res *v1.UserRegisterRes, err error) { + id, err := account.Register(ctx) + if err != nil { + return nil, err + } + return &v1.UserRegisterRes{ + Id: int32(id), + }, nil +} + +func (*Controller) UserLogin(ctx context.Context, req *v1.UserLoginReq) (res *v1.UserLoginRes, err error) { + token, err := account.Login(ctx) + if err != nil { + return nil, err + } + return &v1.UserLoginRes{ + Token: token, + }, nil +} + +func (*Controller) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) { + data, err := account.Info(ctx, req.Token) + if err != nil { + return nil, err + } + return &v1.UserInfoRes{ + User: &pbentity.Users{ + Id: uint32(data.Id), + Username: data.Username, + Password: data.Password, + Email: data.Email, + CreatedAt: timestamppb.New(data.CreatedAt.Time), + UpdatedAt: timestamppb.New(data.UpdatedAt.Time), + }, + }, nil +} +``` diff --git "a/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" new file mode 100644 index 00000000000..3d7c77c6804 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/2.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" @@ -0,0 +1,144 @@ +## cmd引入控制器 +--- +和单体服务一样,微服务也需要在`cmd`中引入。 + +*app/user/internal/cmd/cmd.go* +```go +package cmd + +import ( + "context" + + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/os/gcmd" + "google.golang.org/grpc" + "proxima/app/user/internal/controller/account" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "user grpc service", + Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { + c := grpcx.Server.NewConfig() + c.Options = append(c.Options, []grpc.ServerOption{ + grpcx.Server.ChainUnary( + grpcx.Server.UnaryValidate, + )}..., + ) + s := grpcx.Server.New(c) + account.Register(s) + s.Run() + return nil + }, + } +) +``` + +## 主入口文件 +--- +在入库文件内引入数据库驱动和`cmd`。 + +*app/user/main.go* +```go +package main + +import ( + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + "github.com/gogf/gf/v2/os/gctx" + "proxima/app/user/internal/cmd" +) + +func main() { + cmd.Main.Run(gctx.GetInitCtx()) +} +``` + +## 配置文件 +--- +*app/user/manifest/config/config.yaml* +```go +grpc: + name: "user" # 服务名称 + address: ":32001" # 自定义服务监听地址 + +database: + default: + link: "mysql:root:12345678@tcp(srv.com:3306)/user" + debug: true +``` + +`gprc`字段定义了两个字段,微服务名称和监听端口。这两个是必要的,完整配置见[配置模板](https://goframe.org/docs/micro-service/config#%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF)。 + +## 启动运行 +--- +切换到根目录,确保所有的依赖正常。 + +```bash +$ cd ../../ +go mod tidy +``` + +回到微服务仓库,正式运行微服务项目。 + +```go +$ cd app/user +gf run .\main.go +build: .\main.go +go build -o .\main.exe .\main.go +.\main.exe +build running pid: 15480 +2024-12-06 15:02:01.246 [DEBU] {d8e6fef56e840e1815d0325bc73eda8f} set default registry using file registry as no custom registry set, path: C:\Users\half\AppData\Local\Temp\gsvc +2024-12-06 15:02:01.269 [DEBU] {d8e6fef56e840e1815d0325bc73eda8f} service register: &{Head: Deployment: Namespace: Name:user Version: Endpoints:192.168.10.91:32001 Metadata:map[protocol:grpc]} +2024-12-06 15:02:01.270 [INFO] {d8e6fef56e840e1815d0325bc73eda8f} pid[15480]: grpc server started listening on [:32001] +``` + +至此,**比邻英语本**的第一个微服务开发完成,和单体服务其实相差不大。 + +> 在测试工具中请求`gRPC`时,需要使用`proto`协议文件,请注意填写正确的依赖路径。 + +测试结果展示: + +```json +grpc 127.0.0.1:32001.account.v1.Account.UserRegister +{ +    "username": "oldme", +    "password": "123456", +    "email": "tyyn1022@gmail.com" +} +{ +    "id": 1 +} + +grpc 127.0.0.1:32001.account.v1.Account.UserLogin +{ +    "username": "oldme", +    "password": "123456" +} +{ +    "token": "I am token" +} + +grpc 127.0.0.1:32001.account.v1.Account.UserInfo +{ +    "token": "I am token" +} +{ + +    "user": { +        "Id": 1, +        "Username": "oldme", +        "Password": "123456", +        "Email": "tyyn1022@gmail.com", +        "CreatedAt": { +            "seconds": "1733407200", +            "nanos": 0 +        }, +        "UpdatedAt": { +            "seconds": "1733407200", +            "nanos": 0 +        } +    } +} +```