diff --git a/docs/course/proxima-book/assets/architecture.png b/docs/course/proxima-book/assets/architecture.png new file mode 100644 index 00000000000..768f3655437 Binary files /dev/null and b/docs/course/proxima-book/assets/architecture.png differ diff --git a/docs/course/proxima-book/assets/coffee.jpg b/docs/course/proxima-book/assets/coffee.jpg new file mode 100644 index 00000000000..8113f4f55f1 Binary files /dev/null and b/docs/course/proxima-book/assets/coffee.jpg differ diff --git a/docs/course/proxima-book/proxima-book.md b/docs/course/proxima-book/proxima-book.md new file mode 100644 index 00000000000..6a8786fdee7 --- /dev/null +++ b/docs/course/proxima-book/proxima-book.md @@ -0,0 +1,37 @@ +--- +slug: '/course/proxima-book' +title: '微服务实战教程-比邻英语本' +hide_title: true +sidebar_position: 1 +keywords: [GoFrame,GoFrame框架,比邻英语本,微服务,gRPC,etcd,服务注册,GoFrame微服务,Golang微服务,Golang教程,编程技巧,项目开发,程序员入门,技术栈,软件开发,计算机科学] +description: '本书通过GoFrame框架,以比邻英语本项目实战为例,帮助读者快速掌握GoFrame微服务开发。适合已经对`GoFrame`小有所成,希望进一步掌握微服务开发的朋友。' +--- + +## 本书简介 +--- +**比邻英语本**是 GoFrame 中级实战教程。与初级教程[星辰英语本](https://goframe.org/course/starbook)不同,**微服务**开发是本书的主旋律。 + +## 编写本书的动机 +--- +网上的教程总是哐当一下,甩出各类技术名词、成吨的架构分层,或者不明觉厉的一大段说明。看的人眼花缭乱,往往花了很久的时间,读了一大堆文章,也没能写出一行代码。 + +所以呐,耳闻之不如目见之,目见之不如足践之。实际操作才是掌握微服务的良药,本书将从`GoFrame`框架出发,开发出一个实际的微服务项目,揭开它神秘的面纱。最终您会发现,微服务的开发是一件很简单的事情。当然,这并不是说微服务很简单,只是它的复杂度不是来源于开发,而是微服务治理。 + +以实际项目为导向,分享更多专业、实用的编程技巧和经验,让您学有所成是作者的期望! + +## 目标读者 +--- +已经对`GoFrame`小有所成,希望进一步掌握微服务开发的朋友。 + +## 联系作者 +--- +在编写本书的过程中,不可避免的会有一些错误或者不足之处,如果您有任何问题或者建议,可以在下方留言,也可以联系我,我会尽快回复您! +- 邮箱: `tyyn1022@gmail.com` `tyyn1022@163.com` +- 网站: [https://oldme.net](https://oldme.net) +- 微信: `NobodyIsRight` 来者请备注来意哦! + +## 遇到问题 +--- +在开发中,遇到各种问题都是正常的。如何解决问题才是关键,在遇到问题时,请先尝试自行解决。查阅`GoFrame`文档,使用搜索引擎都是不错的解决问题的方式。如果实在无法解决,可以联系我,我会尽力帮助您解决问题。 + +import DocCardList from '@theme/DocCardList'; \ 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.1.\345\206\231\344\275\234\347\272\246\345\256\232.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.1.\345\206\231\344\275\234\347\272\246\345\256\232.md" new file mode 100644 index 00000000000..63f9f71caed --- /dev/null +++ "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.1.\345\206\231\344\275\234\347\272\246\345\256\232.md" @@ -0,0 +1,37 @@ +--- +title: '1.1.写作约定' +hide_title: true +slug: '/course/proxima-book/about-convention' +keywords: [GoFrame, proxima-book, writing convention, code examples, command line usage, code simplification, microservices development] +description: "本章介绍了 GoFrame 微服务教程的写作约定,包括代码示例的简化原则、命令行使用规范和代码省略说明,帮助读者更好地理解教程内容。" +--- + +## 代码从简 +--- +能看到本书,就说明您已经是代码江湖上颇具水平的老鸟了。所以,我也会减少不必要的啰嗦,跳过不必要的代码细节,**只着重展示微服务的开发流程与特性。** + +## 命令行 +--- +本书会在一些地方使用命令行,我将使用 `$` 符号作为提示符,您不需要输入这个符号。比如,如果我写了 `$ echo "Hello, GoFrame!" `,您只需要输入 `echo "Hello, GoFrame!" ` 即可。 + +```bash +$ echo "Hello, GoFrame!" +Hello, GoFrame! +``` + +## 代码省略 +--- +为了保证篇幅的整洁,在不必要的竖版代码中,我会使用`...`来省略代码。 + +```go +package main + +import "fmt" + +... + +func main() { + fmt.Println("Hello GoFrame") +} + +... 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.2.\346\236\266\346\236\204\344\273\213\347\273\215.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.2.\346\236\266\346\236\204\344\273\213\347\273\215.md" new file mode 100644 index 00000000000..c7f51d70bd9 --- /dev/null +++ "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.2.\346\236\266\346\236\204\344\273\213\347\273\215.md" @@ -0,0 +1,34 @@ +--- +title: '1.2.架构介绍' +hide_title: true +slug: '/course/proxima-book/about-arch' +keywords: [GoFrame, microservices, architecture design, API gateway, user service, word service, gRPC, HTTP, load balancing, authentication] +description: "介绍比邻英语本项目的微服务架构设计,包括用户服务和单词服务的功能拆分,以及API网关的角色和功能,详细说明了微服务间的通信方式和网关的核心职责。" +--- + + +**比邻英语本**是一个帮助用户学习英语单词的轻量级软件,它包含以下业务功能: +- 用户注册; +- 用户登录; +- 用户信息查询; +- 单词的增删改查。 + +我们将这些服务简单地拆分,将同类功能放在一起,演化出两个微服务: +- 用户服务:处理用户注册、登录、信息查询等功能; +- 单词服务:提供单词相关的功能,比如增删改查。 + +微服务不直接对外提供服务,而是统一交由网关处理。网关作为一个`Web`服务,它不直接提供具体的业务功能,而是负责接收请求,转发到各微服务,最后拼接数据返回,以此来完成业务功能。 + +网关的功能不仅限于协议转换,还包括负载均衡、认证授权、日志记录、监控和限流等。微服务之间通常使用`HTTP`或`gRPC`进行通讯。 + +本书微服务采用`gRPC`协议。 + +![](../assets/architecture.png) + +## 代码仓库 +--- +微服务将单体服务拆开,代码也自然分离。它的代码仓库,有两种常见的管理方式: +- **Multirepo:** 多仓库模式,每个微服务都有独立的仓库。优点是每个仓库相对较小,易于管理。缺点是需要额外的工具,流程协调各个服务之间的依赖和版本。 +- **Monorepo:** 单一仓库模式,所有微服务的代码都存放在一个仓库中。优点是可以统一管理版本和依赖,缺点是仓库可能会变得庞大,管理复杂度增加。 + +我们的项目采用`Monorepo`模式。`Multirepo`模式下一个服务一个目录,无需多言。 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.3.\347\216\257\345\242\203\345\207\206\345\244\207.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.3.\347\216\257\345\242\203\345\207\206\345\244\207.md" new file mode 100644 index 00000000000..e70b757ec5b --- /dev/null +++ "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.3.\347\216\257\345\242\203\345\207\206\345\244\207.md" @@ -0,0 +1,97 @@ +--- +title: '1.3.环境准备' +hide_title: true +slug: '/course/proxima-book/about-prepare' +keywords: [GoFrame, gRPC, Protocol Buffers, development environment, installation guide, etcd, microservices tools, Go installation] +description: "详细介绍了开发 GoFrame 微服务项目所需的环境准备工作,包括 Go 语言环境配置、GoFrame 框架安装、gRPC 工具链配置以及相关依赖组件的安装说明。" +--- + +如果您的版本和我不一致,也无需担心,他们基本是共通的。 + +## GoFrame +--- +`Golang`和`GoFrame`的安装方式不再赘述。这里使用的版本信息如下: +- `go version go1.23.4 windows/amd64` +- `goframe v2.8.2` + +## 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` 文件定义消息结构,然后通过编译器生成相应语言的代码。 + +根据不同的操作系统,在 [Protocal Release](https://github.com/protocolbuffers/protobuf/releases) 下载对应的文件安装。如果是 `MacOS` 环境,可以使用 `brew` 工具安装依赖: + +```bash +$ brew install grpc protoc-gen-go protoc-gen-go-grpc +``` + +检测是否安装成功。 +```bash +$protoc --version +libprotoc 26.1 +``` + +## etcd +--- +etcd 是一个分布式键值存储系统,常用于分布式系统中的服务发现。安装它有多种方式,这里贴出 +`docker-compose.yaml`文件用作参考。 + +```yaml +version: "3.7" + +services: + etcd: + image: "bitnami/etcd:3.5" + container_name: "etcd" + restart: "always" + ports: + - 2379:2379 + environment: + - TZ=Asia/Shanghai + - ALLOW_NONE_AUTHENTICATION=yes + - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379 +``` + +如果安装成功,在浏览器访问 [http://IP:2379/version](http://IP:2379/version),会出现以下信息: +```json +{"etcdserver": "3.5.17","etcdcluster": "3.5.0"} +``` + +如果您想更酷一些,安装个`etcd`集群或者学习一下`etcd`的基础使用,可以参考[此文](https://oldme.net/article/32)。 + +## 数据库 +--- +`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" new file mode 100644 index 00000000000..0d646fdf1d8 --- /dev/null +++ "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" @@ -0,0 +1,70 @@ +--- +title: '1.4.项目初始化' +hide_title: true +slug: '/course/proxima-book/about-init' +keywords: [GoFrame, project initialization, Monorepo, project structure, dependency management, go.mod, microservices setup] +description: "详细说明了如何使用 GoFrame CLI 工具初始化微服务项目,包括创建 Monorepo 仓库、配置依赖版本、设置项目结构等关键步骤。" +--- + +## 初始化仓库 +--- +执行命令,初始化一个名为`proxima`的`Monorepo`仓库: + +```bash +$ gf init proxima -m +``` + +修改`go`语言的最低依赖版本为当前环境,确保大于`GoFrame`最低版本要求即可。 + +*go.mod* +```text +module proxima + +go 1.23.4 +``` + +`GoFrame`升级到最新版本: +```bash +$ cd proxima +gf up +``` + +删除不必要的示例文件: +```bash +$ rm -rf app/* +``` + +完成后的项目结构: +```text +app +hack + hack.mk + hack-cli.mk +utility +go.mod +go.sum +``` + +在`Monorepo`仓库模式下,根目录只提供对项目依赖管理,不存在`main.go`文件。 + +`app`目录保存微服务各自的代码文件,例如`app/user/main.go`,`app/word/main.go`。 + +## 安装微服务组件 +--- +安装`grpcx`组件,让`GoFrame`支持微服务开发。 +```bash +$ go get -u github.com/gogf/gf/contrib/rpc/grpcx/v2 +``` + +## 安装数据库驱动 +--- +和单体服务一样,也需要安装对应的数据库驱动,这里演示的是`MySQL`。 +```bash +$ go get -u github.com/gogf/gf/contrib/drivers/mysql/v2 +``` + +## 安装etcd组件 +--- +安装`etcd`组件,提供服务注册功能。 +```bash +$ go get -u github.com/gogf/gf/contrib/registry/etcd/v2 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.5.\346\234\254\344\271\246\346\272\220\347\240\201.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.5.\346\234\254\344\271\246\346\272\220\347\240\201.md" new file mode 100644 index 00000000000..5dfcfcd2ec6 --- /dev/null +++ "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.5.\346\234\254\344\271\246\346\272\220\347\240\201.md" @@ -0,0 +1,34 @@ +--- +title: '1.5.本书源码' +hide_title: true +slug: '/course/proxima-book/about-source' +keywords: [GoFrame, source code, GitHub repository, MIT license, open source, proxima project] +description: "提供了本教程项目的源代码获取方式,包括 GitHub 仓库地址和 MIT 开源许可证的详细说明。" +--- + +本书的源码开源在 [https://github.com/oldme-git/proxima](https://github.com/oldme-git/proxima)。 + +开源许可证基于`MIT`协议: +```text +MIT License + +Copyright (c) 2024 oldme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` \ 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/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257.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/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257.md" new file mode 100644 index 00000000000..c3f772b9a93 --- /dev/null +++ "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/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257.md" @@ -0,0 +1,20 @@ +--- +title: '第一章-基础信息' +hide_title: true +sidebar_position: 1 +slug: '/course/proxima-book/about' +--- + +import DocCardList from '@theme/DocCardList'; + + + + + + + + + + + + diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" new file mode 100644 index 00000000000..a7b600a3772 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" @@ -0,0 +1,87 @@ +--- +title: '3.1.前置准备' +hide_title: true +slug: '/course/proxima-book/word-prepare' +keywords: [GoFrame, word service setup, microservice initialization, database configuration, project structure, service preparation] +description: "详细介绍了单词服务的初始化过程,包括使用 GoFrame CLI 创建服务、配置数据库连接、设置项目结构等基础准备工作。" +--- + +搞定了第一个微服务后,第二个微服务开发起来自然轻车熟路。 + +## 代码初始化 +--- +执行以下命令,建立名为`word`的服务,同样保存在`app`目录下。 + +```bash +$ gf init app/word -a +initializing... +initialization done! +you can now run "cd app/word && gf run main.go" to start your journey, enjoy! +``` + +如法炮制,删除下列文件,留下一个空白的环境。 +```text +app/word/api/* +app/word/internal/controller/* +app/word/internal/cmd/cmd.go +``` + +进入微服务仓库,开始正式开发。 +```bash +$ cd app/word +``` + +## 生成数据模型 +--- +### 建立数据表 +在`word`数据库下,执行`SQL`语句,建立保存用户数据的表: +```sql +CREATE TABLE words ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + uid INT UNSIGNED NOT NULL, + word VARCHAR ( 255 ) NOT NULL, + definition TEXT, + example_sentence TEXT, + chinese_translation VARCHAR ( 255 ), + pronunciation VARCHAR ( 255 ), + created_at DATETIME, + updated_at DATETIME +); +``` + +### 生成dao模型 +*app/user/hack/config.yaml* +```yaml +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/word" + descriptionTag: true +``` + +```bash +$ gf gen dao +generated: D:\project\proxima\app\word\internal\dao\words.go +generated: D:\project\proxima\app\word\internal\dao\internal\words.go +generated: D:\project\proxima\app\word\internal\model\do\words.go +generated: D:\project\proxima\app\word\internal\model\entity\words.go +done! +``` + +### 生成pbentity模型 +*app/user/hack/config.yaml* +```bash +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/word" + descriptionTag: true + + pbentity: + - link: "mysql:root:12345678@tcp(srv.com:3306)/word" +``` + +```bash +$ gf gen pbentity +generated: D:\project\proxima\app\word\manifest\protobuf\pbentity\words.proto +done! diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" new file mode 100644 index 00000000000..221a1f96384 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" @@ -0,0 +1,39 @@ +--- +title: 3.2.业务逻辑 +hide_title: true +slug: /course/proxima-book/word-logic +keywords: [GoFrame, business logic, word management, CRUD operations, microservices logic, vocabulary service] +description: "介绍了单词服务的核心业务逻辑实现,包括单词的创建、查询等基本功能的代码实现。" +--- + +和前文一样,这里简单做个样子 + +*app/word/internal/logic/words/words.go* +```go +package words + +import ( + "context" + + "github.com/gogf/gf/v2/os/gtime" + "proxima/app/word/internal/model/entity" +) + +func Create(ctx context.Context) (id uint, err error) { + return 1, nil +} + +func Get(ctx context.Context) (word *entity.Words, err error) { + return &entity.Words{ + Id: 1, + Uid: 1, + Word: "hello", + Definition: "used as a greeting when you meet somebody.", + ExampleSentence: "Hello, I am oldme!", + ChineseTranslation: "你好", + Pronunciation: "həˈləʊ", + CreatedAt: gtime.New("2024-12-05 22:00:00"), + UpdatedAt: gtime.New("2024-12-05 22:00:00"), + }, nil +} +``` diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" new file mode 100644 index 00000000000..24e2fc3ff5d --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" @@ -0,0 +1,42 @@ +--- +title: '3.3.协议文件' +hide_title: true +slug: '/course/proxima-book/word-protocol' +keywords: [GoFrame, Protocol Buffers, gRPC, API definition, word service protocol, microservices communication] +description: "介绍了单词服务的 Protocol Buffers 协议文件定义,包括创建和获取单词等接口的协议设计和实现。" +--- + +这里我就从简了,省略了更新,删除等操作,只写了`Create`和`Get`,用作示例。 + +*app/word/manifest/protobuf/words/v1/words.proto* +```go +syntax = "proto3"; + +package words.v1; + +option go_package = "proxima/app/word/api/words/v1"; + +import "pbentity/words.proto"; + +service Words{ + rpc Create(CreateReq) returns (CreateRes) {} + rpc Get(GetReq) returns (GetRes) {} +} + +message CreateReq { + uint32 uid = 1; // v:required + string word = 2; // v:required + string definition = 3; // v:required +} + +message CreateRes { + uint32 id = 1; +} + +message GetReq { + uint32 id = 1; // v:required +} + +message GetRes { + pbentity.Words words = 1; +} \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.4.\346\216\247\345\210\266\345\231\250.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.4.\346\216\247\345\210\266\345\231\250.md" new file mode 100644 index 00000000000..85678b5656f --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.4.\346\216\247\345\210\266\345\231\250.md" @@ -0,0 +1,65 @@ +--- +title: "3.4.控制器" +hide_title: true +slug: '/course/proxima-book/word-controller' +keywords: [GoFrame, controller generation, gRPC controller, word service controller, protobuf generation, service implementation] +description: "详细介绍了如何生成和实现单词服务的控制器,包括使用 GoFrame 的代码生成工具和实现具体的业务逻辑。" +--- + +执行命令,生成控制器: + +```bash +$ gf gen pb +``` + +编写逻辑,调用单词微服务: + +*app/word/internal/controller/words/words.go* +```go +package words + +import ( + "context" + + "proxima/app/word/api/pbentity" + v1 "proxima/app/word/api/words/v1" + "proxima/app/word/internal/logic/words" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" +) + +type Controller struct { + v1.UnimplementedWordsServer +} + +func Register(s *grpcx.GrpcServer) { + v1.RegisterWordsServer(s.Server, &Controller{}) +} + +func (*Controller) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) { + id, err := words.Create(ctx) + if err != nil { + return nil, err + } + return &v1.CreateRes{Id: uint32(id)}, nil +} + +func (*Controller) Get(ctx context.Context, req *v1.GetReq) (res *v1.GetRes, err error) { + data, err := words.Get(ctx) + if err != nil { + return nil, err + } + return &v1.GetRes{ + Words: &pbentity.Words{ + Id: uint32(data.Id), + Uid: uint32(data.Uid), + Word: data.Word, + Definition: data.Definition, + ExampleSentence: data.ExampleSentence, + ChineseTranslation: data.ChineseTranslation, + Pronunciation: data.Pronunciation, + CreatedAt: timestamppb.New(data.CreatedAt.Time), + UpdatedAt: timestamppb.New(data.CreatedAt.Time), + }, + }, nil +} +``` diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" new file mode 100644 index 00000000000..8ddb6095e2f --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" @@ -0,0 +1,132 @@ +--- +title: '3.5.启动运行' +hide_title: true +slug: '/course/proxima-book/word-run' +keywords: [GoFrame, gRPC service, word service startup, microservices deployment, service registration, etcd integration] +description: "详细说明了如何启动和运行单词微服务,包括配置服务注册、gRPC 服务设置和健康检查等关键步骤。" +--- + +## cmd引入控制器 +--- +*app/word/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/word/internal/controller/words" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "word 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) + words.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/word/internal/cmd" +) + +func main() { + cmd.Main.Run(gctx.GetInitCtx()) +} +``` + +## 配置文件 +--- +*app/user/manifest/config/config.yaml* +```go +grpc: + name: "word" + address: ":32002" + +database: + default: + link: "mysql:root:12345678@tcp(srv.com:3306)/word" + debug: true +``` + +## 启动运行 +--- +确保依赖正常,运行单词微服务。 + +```go +$ cd app/word +build: .\main.go +go build -o .\main.exe .\main.go +.\main.exe +build running pid: 2416 +2024-12-09 15:10:40.546 [DEBU] {18cc6c8aa5700f18bf2deb5e3439664a} set default registry using file registry as no custom registry set, path: C:\Users\half\AppData\Local\Temp\gsvc +2024-12-09 15:10:40.566 [DEBU] {18cc6c8aa5700f18bf2deb5e3439664a} service register: &{Head: Deployment: Namespace: Name:word Version: Endpoints:192.168.10.98:32002 Metadata:map[protocol:grpc]} +2024-12-09 15:10:40.567 [INFO] {18cc6c8aa5700f18bf2deb5e3439664a} pid[2416]: grpc server started listening on [:32002] +``` + +至此,**比邻英语本**的第二个微服务开发完成。 + +## 测试结果 +--- +```json +grpc 127.0.0.1:32002.words.v1.Words.Create +{ +  "uid": 1, +  "word": "hello", +  "definition": "used as a greeting when you meet somebody." +} +{ +  "id": 1 +} + +grpc 127.0.0.1:32002.words.v1.Words.Get +{ +  "id": 1 +} +{ +  "words": { +  "Id": 1, +  "Uid": 1, +  "Word": "hello", +  "Definition": "used as a greeting when you meet somebody.", +  "ExampleSentence": "Hello, I am oldme!", +  "ChineseTranslation": "你好", +  "Pronunciation": "həˈləʊ", +  "CreatedAt": { +  "seconds": "1733407200", +  "nanos": 0 +  }, +  "UpdatedAt": { +  "seconds": "1733407200", +  "nanos": 0 +  } +  } +} diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" new file mode 100644 index 00000000000..a21e48efd58 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" @@ -0,0 +1,55 @@ +--- +title: '3.6.服务注册' +hide_title: true +slug: '/course/proxima-book/word-etcd-register' +keywords: [GoFrame, etcd, service registration, word service discovery, microservices registry, configuration] +description: "介绍了如何将单词微服务注册到 etcd 服务注册中心,包括配置文件设置和注册逻辑的实现。" +--- + +添加配置文件: + +*app/word/manifest/config/etcd.yaml* +```yaml +etcd: + address: "srv.com:2379" +``` + +在入口文件添加注册逻辑: + +*app/word/main.go* +```go +package main + +import ( + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + + "proxima/app/word/internal/cmd" +) + +func main() { + var ctx = gctx.New() + conf, err := g.Cfg("etcd").Get(ctx, "etcd.address") + if err != nil { + panic(err) + } + + var address = conf.String() + grpcx.Resolver.Register(etcd.New(address)) + + cmd.Main.Run(ctx) +} +``` + +重新运行项目,再次进入`etcd`容器,执行命令查看: +```bash +$ etcdctl get "" --prefix --keys-only +/service/default/default/user/latest/{IP}:32001 +/service/default/default/word/latest/{IP}:32002 +``` + +可以看见,我们的两个微服务都已经成功注册。接下来,运行网关,即可调用它们。 \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241.md" "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241.md" new file mode 100644 index 00000000000..fd11758015b --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241.md" @@ -0,0 +1,12 @@ +--- +title: '第三章-单词服务' +hide_title: true +sidebar_position: 3 +slug: '/course/proxima-book/word' +keywords: [GoFrame, word service, vocabulary management, microservices, CRUD operations, English learning] +description: "本章详细介绍了基于 GoFrame 框架的单词服务实现,包括单词的增删改查等核心功能,以及与其他微服务的集成。" +--- + +import DocCardList from '@theme/DocCardList'; + + 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" new file mode 100644 index 00000000000..574b5694cff --- /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.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" @@ -0,0 +1,94 @@ +--- +title: '2.1.前置准备' +hide_title: true +slug: '/course/proxima-book/user-overview' +keywords: [GoFrame, microservice setup, user service initialization, database configuration, project structure, service preparation] +description: "本节介绍了用户服务的前置准备工作,包括使用 GoFrame CLI 初始化服务、配置数据库连接、设置项目结构等基础内容。" +--- + +微服务开发的大部分内容,都和单体服务一致,甚至比单体服务简单许多。本章是一些基础准备,想必各位都驾轻就熟。 + +## 代码初始化 +--- +`GoFrame`为我们准备好了初始化微服务仓库的命令。执行以下命令,建立名为`user`的服务,并保存在`app`目录下。 + +```bash +$ gf init app/user -a +initializing... +initialization done! +you can now run "cd app/user && gf run main.go" to start your journey, enjoy! +``` + +成功后,会在`app`下建立一个微服务。其实和单体服务没什么两样,就是缺少了`go.mod`和`go.sum`文件。 + +将下列文件全部删除,留下一个空白的环境。 +```text +app/user/api/* +app/user/internal/controller/* +app/user/internal/cmd/cmd.go +``` + +完成后,进入微服务仓库,开始正式开发。 +```bash +$ cd app/user +``` + +## 生成数据模型 +--- +### 建立数据表 +在`user`数据库下,执行`SQL`语句,建立保存用户数据的表: +```sql +CREATE TABLE users ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL, + password CHAR(32) NOT NULL, + email VARCHAR(100), + created_at DATETIME, + updated_at DATETIME +); +``` + +### 生成dao模型 +*app/user/hack/config.yaml* +```yaml +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/user" + descriptionTag: true +``` + +```bash +$ gf gen dao +generated: D:\project\proxima\app\user\internal\dao\users.go +generated: D:\project\proxima\app\user\internal\dao\internal\users.go +generated: D:\project\proxima\app\user\internal\model\do\users.go +generated: D:\project\proxima\app\user\internal\model\entity\users.go +done! +``` + +> 注意,应该在微服务仓库下执行`gf gen dao`命令,也就是`app/user`目录下,这里不要弄错了。后续其他的相关操作也类似。 + +### 生成pbentity模型 +*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`微服务之间的通讯。 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..2bf124fa3a2 --- /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,41 @@ +--- +title: '2.2.业务逻辑' +hide_title: true +slug: '/course/proxima-book/user-logic' +keywords: [GoFrame, business logic, user registration, account management, microservices logic, user service implementation] +description: "详细说明了用户服务中的业务逻辑实现,包括用户注册等核心功能的代码实现和最佳实践。" +--- + +微服务的业务逻辑存放在`*/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.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..25a8af76cd3 --- /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,109 @@ +--- +title: '2.3.协议文件' +hide_title: true +slug: '/course/proxima-book/user-protocol' +keywords: [GoFrame, Protocol Buffers, gRPC, API definition, microservices communication, proto files, user service protocol] +description: "介绍了用户服务中的 Protocol Buffers 协议文件定义,包括用户注册、登录等接口的协议设计,以及 gRPC 服务定义的最佳实践。" +--- + +协议文件指的是`*.proto`文件,`proto`是`gRPC`协议通讯的标准,可以类比为`json`和`HTTP`。但是千万不要理解`proto`就是`json`,他们还是有一定区别的:`proto`同时定义“接口”信息和响应参数,请求参数,而`json`就单纯的保存数据。 + +`proto`文件统一存放在`manifest/protobuf`下,和普通的`HTTP`服务一样,使用目录层级来管理接口版本。 + +## 用户注册 +--- +创建一个名为`account`的目录,管理用户账号相关业务。 + +*app/user/manifest/protobuf/account/v1/account.proto* +```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` (字符串类型,编号为1) + - `password` (字符串类型,编号为2) + - `email` (字符串类型,编号为3) +- 定义了一个消息类型`UserRegisterRes`,包含一个字段: + - `id` (整型,编号为1) + +## 用户登录/查询 +--- +照葫芦画瓢,我们继续定义用户登录和查询接口。最后的文件内容如下: + +*app/user/manifest/protobuf/account/v1/account.proto* +```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..5c47e0c6123 --- /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,74 @@ +--- +title: "2.4.控制器" +hide_title: true +slug: '/course/proxima-book/user-controller' +keywords: [GoFrame, controller generation, gRPC controller, protobuf generation, microservices controller, service implementation] +description: "详细介绍了如何使用 GoFrame 的代码生成工具生成 gRPC 控制器,以及如何实现用户服务的各项功能接口。" +--- + +`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..4189b87aeef --- /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,153 @@ +--- +title: '2.5.启动运行' +hide_title: true +slug: '/course/proxima-book/user-run' +keywords: [GoFrame, gRPC service, service startup, microservices deployment, service registration, etcd integration] +description: "详细说明了如何启动和运行用户微服务,包括服务注册、gRPC 服务配置、与 etcd 的集成以及服务健康检查等关键步骤。" +--- + +## cmd引入控制器 +--- +和单体服务一样,微服务也需要在`cmd`中引入。不同的是,启动服务由`HTTP`变成了`gRPC`。 + +*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 +        } +    } +} 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.6.\346\234\215\345\212\241\346\263\250\345\206\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.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" new file mode 100644 index 00000000000..b2859398c85 --- /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.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" @@ -0,0 +1,65 @@ +--- +title: 2.6.服务注册 +hide_title: true +slug: /course/proxima-book/user-etcd-register +keywords: [GoFrame, etcd, service registration, service discovery, microservices registry, configuration management] +description: "介绍了如何将用户微服务注册到 etcd 服务注册中心,包括配置文件设置、注册逻辑实现以及服务发现机制的配置。" +--- + +接下来,我们将用户微服务注册到`etcd`,供其他服务调用。 + +添加配置文件,写入`etcd`的访问地址。 + +*app/user/manifest/config/etcd.yaml* +```yaml +etcd: + address: "srv.com:2379" +``` + +在入口文件添加注册逻辑: + +*app/user/main.go* +```go +package main + +import ( + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + + "proxima/app/user/internal/cmd" +) + +func main() { + var ctx = gctx.New() + conf, err := g.Cfg("etcd").Get(ctx, "etcd.address") + if err != nil { + panic(err) + } + + var address = conf.String() + grpcx.Resolver.Register(etcd.New(address)) + + cmd.Main.Run(ctx) +} +``` + +实际上,服务注册的关键代码只有一行,其他都是读取文件配置的代码: +```go +grpcx.Resolver.Register(etcd.New(address)) +``` + +重新运行项目,让代码生效。然后进入`etcd`容器,执行命令查看注册是否成功。 +```bash +$ etcdctl get "" --prefix --keys-only +``` + +这条命令用作查看`etcd`中所有存在的`key`,在其中我们会看到注册的服务: +```text +/service/default/default/user/latest/{IP}:32001 +``` + +> 服务注册可以理解成`DNS`域名解析。配置文件中的服务名称`grpc.name`,它可以类比成域名。 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/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241.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/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241.md" new file mode 100644 index 00000000000..10177a7cffa --- /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/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241.md" @@ -0,0 +1,12 @@ +--- +title: '第二章-用户服务' +hide_title: true +sidebar_position: 2 +slug: '/course/proxima-book/user' +keywords: [GoFrame, user service, microservices, authentication, user management, registration, login, user information] +description: "本章详细介绍了基于 GoFrame 框架的用户服务实现,包括用户注册、登录和信息查询等核心功能的开发流程。" +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git "a/docs/course/proxima-book/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256.md" "b/docs/course/proxima-book/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256.md" new file mode 100644 index 00000000000..5571583a608 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256.md" @@ -0,0 +1,25 @@ +--- +title: 第五章-进一步的学习方向与建议 +hide_title: true +sidebar_position: 99 +slug: /course/proxima-book/appendix +keywords: [GoFrame, microservices learning, authentication, load balancing, service configuration, interceptors, future directions] +description: "本章总结了 GoFrame 微服务开发的进阶学习方向,包括用户认证、多服务调用、拦截器使用、负载均衡策略等关键主题。" +--- + +首先,感谢每个读者能够耐心看完本书!您的三个服务有正常运行吗?像比邻星和他的两个伙伴一样。 +然后,这里还有一些学习方向提供给大家。作者本人水平有限,不足之处还请海涵。希望大家事业能够步步高升,蒸蒸日上! + +## 进一步的学习方向 +--- +- 用户认证授权:在微服务中完善用户认证授权,并在网关服务中使用它; +- 调用多个微服务:在同一个控制器中,调用多个微服务完成业务功能; +- 服务端拦截器:使用`GoFrame`提供的`gRPC`服务端拦截器; +- 负载策略:了解微服务负载均衡; +- 服务配置管理:对接一些配置中心,为微服务提供更灵活的配置方式。 + +## 来杯咖啡 +--- +**感谢每个支持者!** + +![功能清单](../assets/coffee.jpg) diff --git "a/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" new file mode 100644 index 00000000000..9988b25e636 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" @@ -0,0 +1,31 @@ +--- +title: '4.1.前置准备' +hide_title: true +slug: '/course/proxima-book/gateway-prepare' +keywords: [GoFrame, gateway initialization, API Gateway setup, microservices gateway, project structure] +description: "介绍了业务网关的初始化过程,包括使用 GoFrame CLI 创建网关服务、配置项目结构等基础准备工作。" +--- + +业务网关和单体`Web`服务基本一致,不同的地方在于,原来的具体业务逻辑交由调用微服务来实现。 + +## 代码初始化 +--- +执行以下命令,建立名为`gateway`的服务,同样保存在`app`目录下。 + +```bash +$ gf init app/gateway -a +initializing... +initialization done! +you can now run "cd app/gateway && gf run main.go" to start your journey, enjoy! +``` + +删除下列文件,留下一个空白的环境。 +```text +app/word/api/* +app/word/internal/controller/* +app/word/internal/cmd/cmd.go +``` + +进入仓库,开始正式开发。 +```bash +$ cd app/gateway \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.2.\346\216\245\345\217\243\344\270\216\346\216\247\345\210\266\345\231\250.md" "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.2.\346\216\245\345\217\243\344\270\216\346\216\247\345\210\266\345\231\250.md" new file mode 100644 index 00000000000..24710cb3926 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.2.\346\216\245\345\217\243\344\270\216\346\216\247\345\210\266\345\231\250.md" @@ -0,0 +1,79 @@ +--- +title: "4.2.接口与控制器" +hide_title: true +slug: '/course/proxima-book/gateway-controller' +keywords: [GoFrame, API design, gateway controller, HTTP endpoints, request validation, response handling] +description: "详细介绍了业务网关的 API 接口设计和控制器实现,包括用户登录、注册等接口的定义和请求处理逻辑。" +--- + +这一步大家应该很熟悉,就不过多介绍。 + +## Api +--- +*app/gateway/api/user/v1/user.go* +```go +package v1 + +import "github.com/gogf/gf/v2/frame/g" + +type LoginReq struct { + g.Meta `path:"users/login" method:"post" sm:"登录" tags:"用户"` + Username string `json:"username" v:"required|length:3,12"` + Password string `json:"password" v:"required|length:6,16"` +} + +type LoginRes struct { + Token string `json:"token" dc:"在需要鉴权的接口中header加入Authorization: token"` +} +``` + +*app/gateway/api/words/v1/words.go* +```go +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +type CreateReq struct { + g.Meta `path:"words" method:"post" sm:"创建" tags:"单词"` + Word string `json:"word" v:"required|length:1,100" dc:"单词"` + Definition string `json:"definition" v:"required|length:1,300" dc:"单词定义"` +} + +type CreateRes struct { +} + +type DetailReq struct { + g.Meta `path:"words/{id}" method:"get" sm:"详情" tags:"单词"` + Id uint `json:"id" v:"required"` +} + +type DetailRes struct { + Id uint `json:"id"` + Word string `json:"word"` + Definition string `json:"definition"` + ExampleSentence string `json:"exampleSentence"` + ChineseTranslation string `json:"chineseTranslation"` + Pronunciation string `json:"pronunciation"` + CreatedAt *gtime.Time `json:"createdAt"` + UpdatedAt *gtime.Time `json:"updatedAt"` +} +``` + +## Controller +--- +执行命令,生成控制器。 +```bash +$ gf gen ctrl +generated: D:\project\proxima\app\gateway\api\user\user.go +generated: D:\project\proxima\app\gateway\internal\controller\user\user.go +generated: D:\project\proxima\app\gateway\internal\controller\user\user_new.go +generated: D:\project\proxima\app\gateway\internal\controller\user\user_v1_login.go +generated: D:\project\proxima\app\gateway\api\words\words.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words_new.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words_v1_create.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words_v1_detail.go +done! diff --git "a/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.3.\345\220\257\345\212\250\350\277\220\350\241\214.md" "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.3.\345\220\257\345\212\250\350\277\220\350\241\214.md" new file mode 100644 index 00000000000..272e811ae4b --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.3.\345\220\257\345\212\250\350\277\220\350\241\214.md" @@ -0,0 +1,131 @@ +--- +title: "4.3.启动运行" +hide_title: true +slug: '/course/proxima-book/gateway-run' +keywords: [GoFrame, gateway configuration, service startup, HTTP server, OpenAPI, Swagger, logging setup] +description: "详细说明了如何配置和启动业务网关服务,包括服务器配置、OpenAPI 文档、日志设置等关键内容。" +--- + +我们先将服务运行起来,之后再调用微服务。 + +## 编写配置文件 +--- +*app/gateway/manifest/config/config.yaml* +```yaml +server: + address: ":8000" + openapiPath: "/api.json" + swaggerPath: "/swagger" + logger: + path: "./log" + file: "{Y-m-d}.log" + level: "all" + stdout: true +``` + +*app/gateway/manifest/config/etcd.yaml* +```yaml +etcd: + address: "srv.com:2379" +``` + +## CMD文件 +--- +*app/gateway/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" + "proxima/app/gateway/internal/controller/user" + "proxima/app/gateway/internal/controller/words" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "start http gateway server", + Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareHandlerResponse) + group.Group("/v1", func(group *ghttp.RouterGroup) { + group.Group("/", func(group *ghttp.RouterGroup) { + group.Bind(user.NewV1()) + group.Bind(words.NewV1()) + }) + }) + }) + s.Run() + return nil + }, + } +) +``` + +## 启动运行 +--- +*app/gateway/main.go* +```go +package main + +import ( + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + "proxima/app/gateway/internal/cmd" +) + +func main() { + var ctx = gctx.New() + conf, err := g.Cfg("etcd").Get(ctx, "etcd.address") + if err != nil { + panic(err) + } + + var address = conf.String() + grpcx.Resolver.Register(etcd.New(address)) + + cmd.Main.Run(ctx) +} +``` + +```bash +$ gf run .\main.go +build running pid: 16144 +2024-12-10 15:30:30.788 [INFO] pid[16144]: http server started listening on [:8000] +2024-12-10 15:30:30.788 [INFO] {f0f10d9d4ec00f181b7a6f615f39d54b} swagger ui is serving at address: http://127.0.0.1:8000/swagger/ +2024-12-10 15:30:30.789 [INFO] {f0f10d9d4ec00f181b7a6f615f39d54b} openapi specification is serving at address: http://127.0.0.1:8000/api.json +2024-12-10 15:30:30.817 [DEBU] {f0f10d9d4ec00f181b7a6f615f39d54b} service register: &{Head: Deployment: Namespace: Name:default Version: Endpoints:192.168.10.98:8000 Metadata:map[insecure:true protocol:http]} +2024-12-10 15:30:30.900 [DEBU] {f0f10d9d4ec00f181b7a6f615f39d54b} etcd put success with key "/service/default/default/default/latest/192.168.10.98:8000", value "{"insecure":true,"protocol":"http"}", lease "7587883327293376805" + + ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | ALL | /api.json | github.com/gogf/gf/v2/net/ghttp.(*Server).openapiSpec | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | ALL | /swagger/* | github.com/gogf/gf/v2/net/ghttp.(*Server).swaggerUI | HOOK_BEFORE_SERVE +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | POST | /v1/users/login | proxima/app/gateway/internal/controller/user.(*ControllerV1).Login | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | POST | /v1/words | proxima/app/gateway/internal/controller/words.(*ControllerV1).Create | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | GET | /v1/words/{id} | proxima/app/gateway/internal/controller/words.(*ControllerV1).Detail | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- +``` + +进入`etcd`容器,执行命令查看: +```bash +$ etcdctl get "" --prefix --keys-only + +/service/default/default/default/latest/{IP}:8000 +/service/default/default/word/latest/{IP}:32001 +/service/default/default/word/latest/{IP}:32002 +``` + +可以看到,我们的三个服务都注册成功了。 \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.4.gRPC\345\256\242\346\210\267\347\253\257.md" "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.4.gRPC\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 00000000000..bcfa3ee2ab2 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.4.gRPC\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,140 @@ +--- +title: "4.4.封装gRPC客户端" +hide_title: true +slug: '/course/proxima-book/gateway-client' +keywords: [GoFrame, gRPC client, microservices communication, client configuration, service discovery, etcd integration] +description: "详细介绍了如何在业务网关中封装和配置 gRPC 客户端,实现与微服务的通信,包括客户端初始化、服务发现等功能。" +--- + +## 客户端 +--- +业务网关相当于`gRPC`客户端,各个微服务相当于`gRPC`服务端。我们将在控制器属性里,定义`gRPC client`,供后续使用。 + +定义`client`由`grpcx.Client.MustNewGrpcClientConn(service, opts...)`完成。 + +*app/gateway/internal/controller/user/user_new.go* +```go +package user + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/user" + v1 "proxima/app/user/api/account/v1" +) + +type ControllerV1 struct { + AccountClient v1.AccountClient +} + +func NewV1() user.IUserV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("user") + + return &ControllerV1{ + AccountClient: v1.NewAccountClient(conn), + } +} +``` + +*app/gateway/internal/controller/words/words_new.go* +```go +package words + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/words" + v1 "proxima/app/word/api/words/v1" +) + +type ControllerV1 struct { + WordsClient v1.WordsClient +} + +func NewV1() words.IWordsV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("word") + + return &ControllerV1{ + WordsClient: v1.NewWordsClient(conn), + } +} +``` + +## 拦截器 +--- +当前客户端没有超时处理,`gRPC`的默认超时时间阈值又非常大。如果`gRPC`服务端、`etcd`服务,或者网络出现异常,业务网关会无限卡死。我们来添加超时拦截器,以应对这种情况。 + +### 定义拦截器 +超时机制很简单,通过`Go`的上下文提供。 + +*app/gateway/utility/grpc.go* +```go +package utility + +import ( + "context" + "time" + "google.golang.org/grpc" +) + +func GrpcClientTimeout(ctx context.Context, method string, req, reply interface{}, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, +) error { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + + err := invoker(ctx, method, req, reply, cc, opts...) + return err +} +``` + +### 调用拦截器 + +*app/gateway/internal/controller/user/user_new.go* +```go +package user + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/user" + "proxima/app/gateway/utility" + v1 "proxima/app/user/api/account/v1" +) + +type ControllerV1 struct { + AccountClient v1.AccountClient +} + +func NewV1() user.IUserV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("user", grpcx.Client.ChainUnary( + utility.GrpcClientTimeout, + )) + + return &ControllerV1{ + AccountClient: v1.NewAccountClient(conn), + } +} +``` + +*app/gateway/internal/controller/words/words_new.go* +```go +package words + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/words" + "proxima/app/gateway/utility" + v1 "proxima/app/word/api/words/v1" +) + +type ControllerV1 struct { + WordsClient v1.WordsClient +} + +func NewV1() words.IWordsV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("word", grpcx.Client.ChainUnary( + utility.GrpcClientTimeout, + )) + + return &ControllerV1{ + WordsClient: v1.NewWordsClient(conn), + } +} \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.5.\350\260\203\347\224\250\345\276\256\346\234\215\345\212\241.md" "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.5.\350\260\203\347\224\250\345\276\256\346\234\215\345\212\241.md" new file mode 100644 index 00000000000..1d4f440ef47 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.5.\350\260\203\347\224\250\345\276\256\346\234\215\345\212\241.md" @@ -0,0 +1,123 @@ +--- +title: 4.5.调用微服务 +hide_title: true +slug: '/course/proxima-book/gateway-call' +keywords: [GoFrame, microservices integration, service invocation, gRPC communication, gateway implementation, service orchestration] +description: "详细说明了如何在业务网关中调用微服务,包括用户服务和单词服务的调用实现,以及业务逻辑的封装方式。" +--- + +接下来,我们要在控制器里调用微服务,完成具体的业务逻辑。 + +> 实际开发中,复杂业务逻辑需要封装到`logic`中,像`Web`单体服务一样,由控制器调用。 + +## 用户服务 +--- +*app/gateway/internal/controller/user/user_v1_login.go* +```go +package user + +import ( + "context" + + account "proxima/app/user/api/account/v1" + + "proxima/app/gateway/api/user/v1" +) + +func (c *ControllerV1) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) { + user, err := c.AccountClient.UserLogin(ctx, &account.UserLoginReq{ + Username: req.Username, + Password: req.Password, + }) + + if err != nil { + return nil, err + } + + return &v1.LoginRes{ + Token: user.GetToken(), + }, nil +} +``` + +发起请求测试,网关调用微服务是否成功: +```bash +$ curl -X POST http://127.0.0.1:8000/v1/users/login \ + -H "Content-Type: application/json" \ + -d '{ + "username": "oldme", + "password": "12345678" + }' + +{ +  "code": 0, +  "message": "", +  "data": { +  "token": "I am token" +  } +} +``` + +看到这个返回结果,恭喜您,大功告成。接下来,您可以尝试把其他的几个服务也开发出来。这里我贴出源码,您可以对照着看看。 + +## 单词服务 +--- +*app/gateway/internal/controller/words/words_v1_create.go* +```go +package words + +import ( + "context" + + words "proxima/app/word/api/words/v1" + + "proxima/app/gateway/api/words/v1" +) + +func (c *ControllerV1) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) { + _, err = c.WordsClient.Create(ctx, &words.CreateReq{ + Uid: 1, + Word: req.Word, + Definition: req.Definition, + }) + + if err != nil { + return nil, err + } + + return &v1.CreateRes{}, nil +} +``` + +*app/gateway/internal/controller/words/words_v1_detail.go* +```go +package words + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gerror" + words "proxima/app/word/api/words/v1" + + "proxima/app/gateway/api/words/v1" +) + +func (c *ControllerV1) Detail(ctx context.Context, req *v1.DetailReq) (res *v1.DetailRes, err error) { + word, err := c.WordsClient.Get(ctx, &words.GetReq{ + Id: uint32(req.Id), + }) + + if err != nil { + return nil, err + } + + if word == nil { + return nil, gerror.New("word not found") + } + + return &v1.DetailRes{ + Id: uint(word.Words.Id), + Word: word.Words.Word, + Definition: word.Words.Definition, + }, nil +} \ No newline at end of file diff --git "a/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263.md" "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263.md" new file mode 100644 index 00000000000..4f8da4c6606 --- /dev/null +++ "b/docs/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263.md" @@ -0,0 +1,12 @@ +--- +title: '第四章-业务网关' +hide_title: true +sidebar_position: 4 +slug: '/course/proxima-book/gateway' +keywords: [GoFrame, API Gateway, microservices gateway, service orchestration, request routing, load balancing] +description: "本章详细介绍了基于 GoFrame 框架实现的业务网关,包括请求路由、服务编排、负载均衡等核心功能的实现。" +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git "a/docs/course/starbook/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200.md" "b/docs/course/starbook/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200.md" index 4f5d56364a8..8f49f815707 100644 --- "a/docs/course/starbook/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200.md" +++ "b/docs/course/starbook/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200/\347\254\254\345\205\255\347\253\240-\351\231\204\350\250\200.md" @@ -7,7 +7,7 @@ keywords: [GoFrame,GoFrame框架,Golang,中间件,JWT机制,配置管理,业务 description: '本书提供了一系列开发功能的优化建议,包括中间件的使用和自定义错误信息,将Jwtkey纳入配置文件,实施JWT黑白名单机制以及在入库数据时进行验证。通过这些方向,读者可以在加深对GoFrame和Golang的理解的同时,提升编程技能,并在业务优化中获得更多经验。' --- -首先,感谢每个读者能够耐心看完本书!然后,这里还有一些建议与方向提供给大家。希望大家能够步步高升,事业蒸蒸日上! +首先,感谢每个读者能够耐心看完本书!然后,这里还有一些学习方向提供给大家。作者本人水平有限,不足之处还请海涵。希望大家事业能够步步高升,蒸蒸日上! ## 进一步的学习方向与建议 --- 在本书所有开发的功能中,作者预留了一些业务优化空间,不妨试试? diff --git "a/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" "b/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" index b766c39d01f..65777ee3c99 100644 --- "a/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" +++ "b/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" @@ -1,7 +1,7 @@ --- slug: '/docs/core/gerror-code-interface' title: '错误处理-错误码接口' -sidebar_position: 1 +sidebar_position: 0 hide_title: true keywords: [GoFrame,GoFrame框架,gcode,错误处理,错误码接口,接口定义,高扩展性,错误码组件,自定义错误码,接口化设计] description: 'GoFrame框架中用于错误处理的错误码接口,基本描述了错误码组件gcode的接口化设计和高扩展性。通过实现Code接口,开发者可以自定义错误码。框架提供了默认实现,开发者也可以根据需求自行扩展,实现自己的错误码逻辑。' diff --git "a/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" "b/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\226\271\346\263\225.md" similarity index 99% rename from "docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" rename to "docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\226\271\346\263\225.md" index 13df5a04657..0fee47ca995 100644 --- "a/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" +++ "b/docs/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\226\271\346\263\225.md" @@ -1,7 +1,7 @@ --- slug: '/docs/core/gerror-code-example' title: '错误处理-错误码使用' -sidebar_position: 0 +sidebar_position: 1 hide_title: true keywords: [GoFrame,GoFrame框架,错误处理,错误码,堆栈信息,NewCode,WrapCode,error,gerror,gcode] description: '在GoFrame框架中使用NewCode和WrapCode方法进行错误处理,通过这些方法可以创建和包裹带有自定义错误码和堆栈信息的error对象。文档提供了基于GoFrame框架的示例代码,帮助开发者更好地理解错误码在程序中的应用。' diff --git a/docs/release/v2.0 2022-03-09.md b/docs/release/v2.0 2022-03-09.md index dc148333be1..f49d2ab06d4 100644 --- a/docs/release/v2.0 2022-03-09.md +++ b/docs/release/v2.0 2022-03-09.md @@ -226,10 +226,10 @@ description: 'GoFrame v2.0版本发布,增强了工程设计和全链路跟踪 2. 增加 `CodeMessage` 方法,用于获取指定错误的错误码信息。 3. 增加 `NewOption` 方法,用于自定义配置的错误对象创建,献给框架高级玩家。 4. 增加 `HasStack` 方法,用于判断给定的error接口对象是否实现(包含)了堆栈信息。 -5. 错误码从整型改为接口对象,以实现可定制性并提高可扩展性,详情参考 `gcode` 组件介绍: [错误处理-错误码特性](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码使用.md) +5. 错误码从整型改为接口对象,以实现可定制性并提高可扩展性,详情参考 `gcode` 组件介绍: [错误处理-错误码特性](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码方法.md) 6. 提高易用性,改进 `NewCode/NewCodeSkip/WrapCode/WrapCodeSkip` 方法,调增 `text` 输入参数为非必须,默认使用对应错误码的 `Message` 信息。 2. `/errors/gcode` -1. 增加 `gcode` 错误码组件,提供可定制型和扩展性极强的错误码管理,结合 `gerror` 组件实现强大的错误处理: [错误处理-错误码使用](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码使用.md) +1. 增加 `gcode` 错误码组件,提供可定制型和扩展性极强的错误码管理,结合 `gerror` 组件实现强大的错误处理: [错误处理-错误码使用](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码方法.md) ### 5、其他组件 diff --git "a/docs/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" "b/docs/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" index 131fced4a3a..f5e9bea16b1 100644 --- "a/docs/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" +++ "b/docs/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" @@ -58,7 +58,7 @@ description: 'GoFrame v1.15 版本提供了一系列的新功能和改进,为 - 完善单元测试。 3. `gerror` 1. 增加 `Newf/NewSkipf` 方法创建错误对象: [错误处理](../../docs/核心组件/错误处理/错误处理.md) - 2. 增加对错误码特性的支持: [错误处理-错误码使用](../../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码使用.md) + 2. 增加对错误码特性的支持: [错误处理-错误码使用](../../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码方法.md) 3. 完善单元测试。 4. `gvalid` - 增加 `phone-loose` 宽松手机号校验规则,只要满足 `13/14/15/16/17/18/19` 开头的 `11` 位数字都可以通过验证。 diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 6cbe9df9407..16bc4c73e9c 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -154,6 +154,11 @@ const config: Config = { label: '实战教程-星辰英语本', to: '/course/starbook', }, + { + sidebarId: 'courseProximaBookSidebar', + label: '微服务实战教程-比邻英语本', + to: '/course/proxima-book', + }, ], }, {to: 'showcase', label: '案例展示', position: 'left'}, @@ -191,7 +196,7 @@ const config: Config = { maxHeadingLevel: 4, }, footer: { - copyright: `Copyright ©${new Date().getFullYear()} GoFrame OpenSource Team`, + copyright: `Copyright ${new Date().getFullYear()} GoFrame OpenSource Team`, }, // 代码块配置 prism: { diff --git a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/assets/architecture.png b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/assets/architecture.png new file mode 100644 index 00000000000..768f3655437 Binary files /dev/null and b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/assets/architecture.png differ diff --git a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/assets/coffee.jpg b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/assets/coffee.jpg new file mode 100644 index 00000000000..8113f4f55f1 Binary files /dev/null and b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/assets/coffee.jpg differ diff --git a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/proxima-book.md b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/proxima-book.md new file mode 100644 index 00000000000..6d97bb40130 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/proxima-book.md @@ -0,0 +1,37 @@ +--- +slug: '/course/proxima-book' +title: 'Microservice Tutorial - Proxima Book' +hide_title: true +sidebar_position: 1 +keywords: [GoFrame, GoFrame Framework, Proxima Notebook, Microservices, gRPC, etcd, Service Registry, GoFrame Microservices, Golang Microservices, Golang Tutorial, Programming Tips, Project Development, Developer Guide, Tech Stack, Software Development, Computer Science] +description: 'This book uses the Proxima Notebook project as a practical example to help readers quickly master GoFrame microservices development. Perfect for developers who are familiar with GoFrame and want to advance into microservices development.' +--- + +## Introduction +--- +The **Proxima Notebook** is an intermediate-level practical tutorial for GoFrame. Unlike the beginner-level tutorial [Star Notebook](https://goframe.org/course/starbook), this book focuses primarily on **microservices** development. + +## Motivation +--- +Many online tutorials overwhelm readers with technical jargon, complex architectural layers, and intimidating explanations. After spending hours reading through numerous articles, developers often find themselves unable to write a single line of code. + +As the saying goes, "Hearing is not as good as seeing, seeing is not as good as doing." Hands-on practice is the best way to master microservices. This book takes you on a journey from the basics of the `GoFrame` framework to building a real microservices project, demystifying the process along the way. You'll discover that developing microservices isn't as complicated as it seems. The real complexity lies not in the development itself, but in microservices governance. + +Our goal is to share professional, practical programming tips and experiences through a project-based approach, helping you achieve real mastery! + +## Target Audience +--- +This book is designed for developers who are already familiar with `GoFrame` and want to advance their skills in microservices development. + +## Contact the Author +--- +While writing this book, some errors or omissions are inevitable. If you have any questions or suggestions, feel free to leave a comment below or contact me directly. I'll respond as soon as possible! +- Email: `tyyn1022@gmail.com` `tyyn1022@163.com` +- Website: [https://oldme.net](https://oldme.net) +- WeChat: `NobodyIsRight` (Please mention your purpose when adding) + +## Troubleshooting +--- +Encountering problems during development is normal. The key is knowing how to solve them. When facing issues, try to resolve them independently first. Consulting the `GoFrame` documentation and using search engines are excellent problem-solving approaches. If you still can't find a solution, feel free to contact me, and I'll do my best to help. + +import DocCardList from '@theme/DocCardList'; \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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.1.\345\206\231\344\275\234\347\272\246\345\256\232.md" "b/i18n/en/docusaurus-plugin-content-docs/current/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.1.\345\206\231\344\275\234\347\272\246\345\256\232.md" new file mode 100644 index 00000000000..1335159a558 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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.1.\345\206\231\344\275\234\347\272\246\345\256\232.md" @@ -0,0 +1,37 @@ +--- +title: '1.1 Writing Conventions' +hide_title: true +slug: '/course/proxima-book/about-convention' +keywords: [GoFrame, proxima-book, writing convention, code examples, command line usage, code simplification, microservices development] +description: "This chapter introduces the writing conventions used in the GoFrame microservices tutorial, including code simplification principles, command line usage standards, and code omission explanations to help readers better understand the tutorial content." +--- + +## Simplified Code Examples +--- +If you're reading this book, you're likely already an experienced developer. Therefore, I'll minimize unnecessary verbosity and skip non-essential code details, **focusing primarily on the microservices development process and its key features.** + +## Command Line Usage +--- +Throughout this book, I'll use the `$` symbol as a command prompt - you don't need to type this symbol. For example, if you see `$ echo "Hello, GoFrame!"`, you should only type `echo "Hello, GoFrame!"`. + +```bash +$ echo "Hello, GoFrame!" +Hello, GoFrame! +``` + +## Code Omissions +--- +To keep the content concise and clean, I'll use `...` to indicate omitted code in vertical code blocks. + +```go +package main + +import "fmt" + +... + +func main() { + fmt.Println("Hello GoFrame") +} + +... diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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" "b/i18n/en/docusaurus-plugin-content-docs/current/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" new file mode 100644 index 00000000000..2c6b6b97648 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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" @@ -0,0 +1,45 @@ +--- +title: '1.2 Architecture Overview' +hide_title: true +slug: '/course/proxima-book/about-arch' +keywords: [GoFrame, microservices, architecture design, API gateway, user service, word service, gRPC, HTTP, load balancing, authentication] +description: "An overview of the Proxima Notebook project's microservices architecture, including the functional separation of user and word services, and the role and responsibilities of the API gateway, detailing inter-service communication methods and core gateway functions." +--- + + +The **Proxima Notebook** is a lightweight application designed to help users learn English vocabulary. It includes the following features: +- User registration +- User login +- User information queries +- Word management (CRUD operations) + +We've organized these features into logical groups, resulting in two microservices: +- User Service: Handles user registration, login, and information queries +- Word Service: Provides word-related functionality, such as CRUD operations + +Rather than exposing microservices directly, all requests are routed through an API gateway. The gateway operates as a web service that doesn't implement business logic directly. Instead, it receives requests, forwards them to appropriate microservices, and aggregates responses to complete business operations. + +The gateway's responsibilities extend beyond protocol translation, including: +- Load balancing +- Authentication and authorization +- Logging +- Monitoring +- Rate limiting + +Microservices typically communicate using either HTTP or gRPC protocols. In this project, we'll be using gRPC. + +![](../assets/architecture.png) + +## Code Repository +--- +When breaking down a monolithic service into microservices, the code naturally separates as well. There are two common approaches to managing the code repository: + +- **Multirepo:** Each microservice has its own repository. + - Advantages: Smaller, more manageable repositories + - Disadvantages: Requires additional tools and processes to coordinate dependencies and versions between services + +- **Monorepo:** All microservices code lives in a single repository. + - Advantages: Unified version and dependency management + - Disadvantages: Repository can become large and complex to manage + +Our project uses the `Monorepo` approach. The `Multirepo` approach, with its one-service-per-directory structure, is straightforward enough to not require further explanation. diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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.3.\347\216\257\345\242\203\345\207\206\345\244\207.md" "b/i18n/en/docusaurus-plugin-content-docs/current/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.3.\347\216\257\345\242\203\345\207\206\345\244\207.md" new file mode 100644 index 00000000000..113db7c608b --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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.3.\347\216\257\345\242\203\345\207\206\345\244\207.md" @@ -0,0 +1,96 @@ +--- +title: '1.3 Environment Setup' +hide_title: true +slug: '/course/proxima-book/about-prepare' +keywords: [GoFrame, gRPC, Protocol Buffers, development environment, installation guide, etcd, microservices tools, Go installation] +description: "A comprehensive guide to setting up the development environment for GoFrame microservices projects, including Go language configuration, GoFrame framework installation, gRPC toolchain setup, and installation instructions for related dependencies." +--- + +Don't worry if your versions differ from mine - the principles remain largely the same. + +## GoFrame +--- +We'll skip the basic installation of `Golang` and `GoFrame`. Here are the versions used in this tutorial: +- `go version go1.23.4 windows/amd64` +- `goframe v2.8.2` + +## gRPC +--- +`gRPC` is a Remote Procedure Call (RPC) framework developed by Google, built on top of HTTP/2. It uses Protocol Buffers as its default serialization format. + +Go provides gRPC functionality through the `gRPC-go` plugin. Install it using these commands: +```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 Testing Tools +After developing gRPC interfaces, you'll need testing tools to verify their functionality. Popular options include `Postman`, `Apifox`, and `Apipost`. They're all similar - choose the one you prefer. + +Throughout this book, we'll display test results in `json` format, like this: +```json +grpc 127.0.0.1:32001.account.v1.Account.UserRegister +{ + "username": "oldme", + "password": "123456", + "email": "tyyn1022@gmail.com" +} +{ + "id": 1 +} +``` + +These represent the request address, request parameters, and response parameters, respectively. + +## Protocol Buffers +--- +Protocol Buffers is Google's data serialization format for structured data. It uses `.proto` files to define message structures, which are then compiled into language-specific code. + +Download the appropriate version for your operating system from [Protocol Buffers Releases](https://github.com/protocolbuffers/protobuf/releases). For MacOS users, you can install dependencies using `brew`: + +```bash +$ brew install grpc protoc-gen-go protoc-gen-go-grpc +``` + +Verify the installation: +```bash +$ protoc --version +libprotoc 26.1 +``` + +## etcd +--- +etcd is a distributed key-value store commonly used for service discovery in distributed systems. There are several ways to install it. Here's a reference `docker-compose.yaml` file: + +```yaml +version: "3.7" + +services: + etcd: + image: "bitnami/etcd:3.5" + container_name: "etcd" + restart: "always" + ports: + - 2379:2379 + environment: + - TZ=Asia/Shanghai + - ALLOW_NONE_AUTHENTICATION=yes + - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379 +``` + +If installed successfully, visiting [http://IP:2379/version](http://IP:2379/version) in your browser should display: +```json +{"etcdserver": "3.5.17","etcdcluster": "3.5.0"} +``` + +For a more advanced setup, like installing an etcd cluster or learning etcd basics, check out [this article](https://oldme.net/article/32). + +## Database +--- +MySQL installation is straightforward, and you can use other databases if preferred. + +Important: In a microservices architecture, each service should have its own database. We'll need to create two databases named `user` and `word`: + +```sql +CREATE DATABASE user; +CREATE DATABASE word; \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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/i18n/en/docusaurus-plugin-content-docs/current/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" new file mode 100644 index 00000000000..904aa13fa58 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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" @@ -0,0 +1,70 @@ +--- +title: '1.4 Project Initialization' +hide_title: true +slug: '/course/proxima-book/about-init' +keywords: [GoFrame, project initialization, Monorepo, project structure, dependency management, go.mod, microservices setup] +description: "A detailed guide on initializing a microservices project using the GoFrame CLI tool, including creating a Monorepo repository, configuring dependencies, and setting up the project structure." +--- + +## Repository Initialization +--- +Use the following command to initialize a Monorepo repository named `proxima`: + +```bash +$ gf init proxima -m +``` + +Update the minimum Go version in your environment to be compatible with GoFrame's requirements. + +*go.mod* +```text +module proxima + +go 1.23.4 +``` + +Upgrade GoFrame to the latest version: +```bash +$ cd proxima +gf up +``` + +Remove unnecessary example files: +```bash +$ rm -rf app/* +``` + +The resulting project structure will look like this: +```text +app +hack + hack.mk + hack-cli.mk +utility +go.mod +go.sum +``` + +In Monorepo mode, the root directory only manages project dependencies and doesn't contain a `main.go` file. + +The `app` directory stores code for each microservice, such as `app/user/main.go` and `app/word/main.go`. + +## Installing Microservice Components +--- +Install the `grpcx` component to enable microservice development with GoFrame: +```bash +$ go get -u github.com/gogf/gf/contrib/rpc/grpcx/v2 +``` + +## Installing Database Drivers +--- +Like monolithic applications, you'll need to install the appropriate database driver. Here we'll demonstrate using MySQL: +```bash +$ go get -u github.com/gogf/gf/contrib/drivers/mysql/v2 +``` + +## Installing etcd Components +--- +Install the `etcd` component for service registration: +```bash +$ go get -u github.com/gogf/gf/contrib/registry/etcd/v2 diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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.5.\346\234\254\344\271\246\346\272\220\347\240\201.md" "b/i18n/en/docusaurus-plugin-content-docs/current/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.5.\346\234\254\344\271\246\346\272\220\347\240\201.md" new file mode 100644 index 00000000000..f8bf47a2bb3 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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.5.\346\234\254\344\271\246\346\272\220\347\240\201.md" @@ -0,0 +1,33 @@ +--- +title: '1.5 Source Code' +hide_title: true +slug: '/course/proxima-book/about-source' +keywords: [GoFrame, source code, GitHub repository, MIT license, open source, proxima project] +description: "Access the tutorial project's source code, including the GitHub repository link and detailed information about the MIT open source license." +--- + +The source code for this book is available on [https://github.com/oldme-git/proxima](https://github.com/oldme-git/proxima). + +The project is licensed under the `MIT` License: +```text +MIT License + +Copyright (c) 2024 oldme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257.md" new file mode 100644 index 00000000000..34d58612188 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257/\347\254\254\344\270\200\347\253\240-\345\237\272\347\241\200\344\277\241\346\201\257.md" @@ -0,0 +1,10 @@ +--- +title: 'Chapter 1 - Basic Information' +hide_title: true +sidebar_position: 1 +slug: '/course/proxima-book/about' +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" new file mode 100644 index 00000000000..faf896fc5d9 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" @@ -0,0 +1,87 @@ +--- +title: '3.1 Prerequisites' +hide_title: true +slug: '/course/proxima-book/word-prepare' +keywords: [GoFrame, word service setup, microservice initialization, database configuration, project structure, service preparation] +description: "A detailed guide on initializing the Word Service, including creating the service using GoFrame CLI, configuring database connections, and setting up the project structure." +--- + +After successfully implementing our first microservice, developing the second one will be more straightforward as we're now familiar with the process. + +## Code Initialization +--- +Execute the following command to create a service named `word` in the `app` directory. + +```bash +$ gf init app/word -a +initializing... +initialization done! +you can now run "cd app/word && gf run main.go" to start your journey, enjoy! +``` + +Following the same process as before, remove the following files to start with a clean environment: +```text +app/word/api/* +app/word/internal/controller/* +app/word/internal/cmd/cmd.go +``` + +Navigate to the microservice directory to begin development: +```bash +$ cd app/word +``` + +## Generating Data Models +--- +### Creating Database Tables +Execute the following SQL statement in the `word` database to create the table for storing word data: +```sql +CREATE TABLE words ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + uid INT UNSIGNED NOT NULL, + word VARCHAR ( 255 ) NOT NULL, + definition TEXT, + example_sentence TEXT, + chinese_translation VARCHAR ( 255 ), + pronunciation VARCHAR ( 255 ), + created_at DATETIME, + updated_at DATETIME +); +``` + +### Generating DAO Models +*app/user/hack/config.yaml* +```yaml +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/word" + descriptionTag: true +``` + +```bash +$ gf gen dao +generated: D:\project\proxima\app\word\internal\dao\words.go +generated: D:\project\proxima\app\word\internal\dao\internal\words.go +generated: D:\project\proxima\app\word\internal\model\do\words.go +generated: D:\project\proxima\app\word\internal\model\entity\words.go +done! +``` + +### Generating Protocol Buffer Entity Models +*app/user/hack/config.yaml* +```bash +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/word" + descriptionTag: true + + pbentity: + - link: "mysql:root:12345678@tcp(srv.com:3306)/word" +``` + +```bash +$ gf gen pbentity +generated: D:\project\proxima\app\word\manifest\protobuf\pbentity\words.proto +done! diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" new file mode 100644 index 00000000000..e0de1fd3074 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.2.\344\270\232\345\212\241\351\200\273\350\276\221.md" @@ -0,0 +1,38 @@ +--- +title: '3.2 Business Logic' +hide_title: true +slug: /course/proxima-book/word-logic +keywords: [GoFrame, business logic, word management, CRUD operations, microservices logic, vocabulary service] +description: "This section covers the implementation of core business logic for the Word Service, including basic functionalities such as word creation and retrieval." +--- + +Similar to our previous implementation, we'll create a simple example here. + +*app/word/internal/logic/words/words.go* +```go +package words + +import ( + "context" + + "github.com/gogf/gf/v2/os/gtime" + "proxima/app/word/internal/model/entity" +) + +func Create(ctx context.Context) (id uint, err error) { + return 1, nil +} + +func Get(ctx context.Context) (word *entity.Words, err error) { + return &entity.Words{ + Id: 1, + Uid: 1, + Word: "hello", + Definition: "used as a greeting when you meet somebody.", + ExampleSentence: "Hello, I am oldme!", + ChineseTranslation: "你好", + Pronunciation: "həˈləʊ", + CreatedAt: gtime.New("2024-12-05 22:00:00"), + UpdatedAt: gtime.New("2024-12-05 22:00:00"), + }, nil +} diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" new file mode 100644 index 00000000000..0ff2c8afc63 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.3.\345\215\217\350\256\256\346\226\207\344\273\266.md" @@ -0,0 +1,42 @@ +--- +title: '3.3 Protocol Files' +hide_title: true +slug: '/course/proxima-book/word-protocol' +keywords: [GoFrame, Protocol Buffers, gRPC, API definition, word service protocol, microservices communication] +description: "This section covers the Protocol Buffers protocol file definitions for the Word Service, including the design and implementation of interfaces for creating and retrieving words." +--- + +For simplicity, we'll focus on the basic `Create` and `Get` operations as examples, omitting update and delete operations. + +*app/word/manifest/protobuf/words/v1/words.proto* +```go +syntax = "proto3"; + +package words.v1; + +option go_package = "proxima/app/word/api/words/v1"; + +import "pbentity/words.proto"; + +service Words{ + rpc Create(CreateReq) returns (CreateRes) {} + rpc Get(GetReq) returns (GetRes) {} +} + +message CreateReq { + uint32 uid = 1; // v:required + string word = 2; // v:required + string definition = 3; // v:required +} + +message CreateRes { + uint32 id = 1; +} + +message GetReq { + uint32 id = 1; // v:required +} + +message GetRes { + pbentity.Words words = 1; +} \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.4.\346\216\247\345\210\266\345\231\250.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.4.\346\216\247\345\210\266\345\231\250.md" new file mode 100644 index 00000000000..23a4019e658 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.4.\346\216\247\345\210\266\345\231\250.md" @@ -0,0 +1,64 @@ +--- +title: "3.4 Controllers" +hide_title: true +slug: '/course/proxima-book/word-controller' +keywords: [GoFrame, controller generation, gRPC controller, word service controller, protobuf generation, service implementation] +description: "A comprehensive guide on generating and implementing controllers for the Word Service, including using GoFrame's code generation tools and implementing specific business logic." +--- + +Execute the following command to generate the controllers: + +```bash +$ gf gen pb +``` + +Now, let's implement the logic for the word microservice: + +*app/word/internal/controller/words/words.go* +```go +package words + +import ( + "context" + + "proxima/app/word/api/pbentity" + v1 "proxima/app/word/api/words/v1" + "proxima/app/word/internal/logic/words" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" +) + +type Controller struct { + v1.UnimplementedWordsServer +} + +func Register(s *grpcx.GrpcServer) { + v1.RegisterWordsServer(s.Server, &Controller{}) +} + +func (*Controller) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) { + id, err := words.Create(ctx) + if err != nil { + return nil, err + } + return &v1.CreateRes{Id: uint32(id)}, nil +} + +func (*Controller) Get(ctx context.Context, req *v1.GetReq) (res *v1.GetRes, err error) { + data, err := words.Get(ctx) + if err != nil { + return nil, err + } + return &v1.GetRes{ + Words: &pbentity.Words{ + Id: uint32(data.Id), + Uid: uint32(data.Uid), + Word: data.Word, + Definition: data.Definition, + ExampleSentence: data.ExampleSentence, + ChineseTranslation: data.ChineseTranslation, + Pronunciation: data.Pronunciation, + CreatedAt: timestamppb.New(data.CreatedAt.Time), + UpdatedAt: timestamppb.New(data.CreatedAt.Time), + }, + }, nil +} diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" new file mode 100644 index 00000000000..bd6cb04f113 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.5.\345\220\257\345\212\250\350\277\220\350\241\214.md" @@ -0,0 +1,132 @@ +--- +title: '3.5 Service Startup' +hide_title: true +slug: '/course/proxima-book/word-run' +keywords: [GoFrame, gRPC service, word service startup, microservices deployment, service registration, etcd integration] +description: "A comprehensive guide on starting and running the Word microservice, including service registration configuration, gRPC service setup, and health check implementation." +--- + +## Importing Controllers in CMD +--- +*app/word/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/word/internal/controller/words" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "word 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) + words.Register(s) + s.Run() + return nil + }, + } +) +``` + +## Main Entry File +--- +Import the database driver and `cmd` in the main entry file. + +*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/word/internal/cmd" +) + +func main() { + cmd.Main.Run(gctx.GetInitCtx()) +} +``` + +## Configuration File +--- +*app/user/manifest/config/config.yaml* +```yaml +grpc: + name: "word" + address: ":32002" + +database: + default: + link: "mysql:root:12345678@tcp(srv.com:3306)/word" + debug: true +``` + +## Starting the Service +--- +Ensure all dependencies are properly installed, then run the word microservice. + +```bash +$ cd app/word +build: .\main.go +go build -o .\main.exe .\main.go +.\main.exe +build running pid: 2416 +2024-12-09 15:10:40.546 [DEBU] {18cc6c8aa5700f18bf2deb5e3439664a} set default registry using file registry as no custom registry set, path: C:\Users\half\AppData\Local\Temp\gsvc +2024-12-09 15:10:40.566 [DEBU] {18cc6c8aa5700f18bf2deb5e3439664a} service register: &{Head: Deployment: Namespace: Name:word Version: Endpoints:192.168.10.98:32002 Metadata:map[protocol:grpc]} +2024-12-09 15:10:40.567 [INFO] {18cc6c8aa5700f18bf2deb5e3439664a} pid[2416]: grpc server started listening on [:32002] +``` + +With this, we've completed the development of the second microservice for Proxima Notebook. + +## Testing Results +--- +```json +grpc 127.0.0.1:32002.words.v1.Words.Create +{ + "uid": 1, + "word": "hello", + "definition": "used as a greeting when you meet somebody." +} +{ + "id": 1 +} + +grpc 127.0.0.1:32002.words.v1.Words.Get +{ + "id": 1 +} +{ + "words": { + "Id": 1, + "Uid": 1, + "Word": "hello", + "Definition": "used as a greeting when you meet somebody.", + "ExampleSentence": "Hello, I am oldme!", + "ChineseTranslation": "你好", + "Pronunciation": "həˈləʊ", + "CreatedAt": { + "seconds": "1733407200", + "nanos": 0 + }, + "UpdatedAt": { + "seconds": "1733407200", + "nanos": 0 + } + } +} diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" new file mode 100644 index 00000000000..1013ffa1cbe --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/3.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" @@ -0,0 +1,55 @@ +--- +title: '3.6 Service Registration' +hide_title: true +slug: '/course/proxima-book/word-etcd-register' +keywords: [GoFrame, etcd, service registration, word service discovery, microservices registry, configuration] +description: "Learn how to register the Word microservice with the etcd service registry, including configuration file setup and registration logic implementation." +--- + +Add the configuration file: + +*app/word/manifest/config/etcd.yaml* +```yaml +etcd: + address: "srv.com:2379" +``` + +Add the registration logic to the entry file: + +*app/word/main.go* +```go +package main + +import ( + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + + "proxima/app/word/internal/cmd" +) + +func main() { + var ctx = gctx.New() + conf, err := g.Cfg("etcd").Get(ctx, "etcd.address") + if err != nil { + panic(err) + } + + var address = conf.String() + grpcx.Resolver.Register(etcd.New(address)) + + cmd.Main.Run(ctx) +} +``` + +Restart the project, enter the `etcd` container again, and execute the following command to verify: +```bash +$ etcdctl get "" --prefix --keys-only +/service/default/default/user/latest/{IP}:32001 +/service/default/default/word/latest/{IP}:32002 +``` + +As we can see, both of our microservices have been successfully registered. Next, we can run the gateway to start calling these services. \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241.md" new file mode 100644 index 00000000000..43cdcfbba1a --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241/\347\254\254\344\270\211\347\253\240-\345\215\225\350\257\215\346\234\215\345\212\241.md" @@ -0,0 +1,12 @@ +--- +title: 'Chapter 3 - Word Service' +hide_title: true +sidebar_position: 3 +slug: '/course/proxima-book/word' +keywords: [GoFrame, word service, vocabulary management, microservices, CRUD operations, English learning] +description: "This chapter provides a detailed guide on implementing the Word Service using the GoFrame framework, covering core functionalities such as CRUD operations for vocabulary management and integration with other microservices." +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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/i18n/en/docusaurus-plugin-content-docs/current/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" new file mode 100644 index 00000000000..373963c4dd0 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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" @@ -0,0 +1,94 @@ +--- +title: '2.1 Prerequisites' +hide_title: true +slug: '/course/proxima-book/user-overview' +keywords: [GoFrame, microservice setup, user service initialization, database configuration, project structure, service preparation] +description: "This section covers the prerequisites for the user service, including service initialization using GoFrame CLI, database configuration, and project structure setup." +--- + +Most aspects of microservice development are similar to monolithic services, and in many cases, even simpler. This chapter covers some basic preparations that should be familiar to everyone. + +## Code Initialization +--- +GoFrame provides a command for initializing microservice repositories. Execute the following command to create a service named `user` in the `app` directory. + +```bash +$ gf init app/user -a +initializing... +initialization done! +you can now run "cd app/user && gf run main.go" to start your journey, enjoy! +``` + +After successful initialization, a microservice will be created in the `app` directory. It's similar to a monolithic service, except that it lacks the `go.mod` and `go.sum` files. + +Delete the following files to start with a clean environment: +```text +app/user/api/* +app/user/internal/controller/* +app/user/internal/cmd/cmd.go +``` + +After completing these steps, enter the microservice repository to begin development. +```bash +$ cd app/user +``` + +## Database Setup +--- +### Creating Tables +In the `user` database, execute the following SQL statement to create a table for storing user data: +```sql +CREATE TABLE users ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL, + password CHAR(32) NOT NULL, + email VARCHAR(100), + created_at DATETIME, + updated_at DATETIME +); +``` + +### Generating DAO Models +*app/user/hack/config.yaml* +```yaml +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(srv.com:3306)/user" + descriptionTag: true +``` + +```bash +$ gf gen dao +generated: D:\project\proxima\app\user\internal\dao\users.go +generated: D:\project\proxima\app\user\internal\dao\internal\users.go +generated: D:\project\proxima\app\user\internal\model\do\users.go +generated: D:\project\proxima\app\user\internal\model\entity\users.go +done! +``` + +> Note: Execute the `gf gen dao` command in the microservice repository (i.e., the `app/user` directory). Be careful not to get this wrong. Similar rules apply to other related operations. + +### Generating Protocol Buffer Entity Models +*app/user/hack/config.yaml* +```yaml +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! +``` + +### Differences Between `gen dao` and `gen pbentity` + +- `gen dao` generates Go files primarily used within the microservice, such as for ORM operations +- `gen pbentity` generates Protocol Buffer files mainly used for gRPC communication between microservices diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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/i18n/en/docusaurus-plugin-content-docs/current/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..89af8801467 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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,41 @@ +--- +title: '2.2 Business Logic' +hide_title: true +slug: '/course/proxima-book/user-logic' +keywords: [GoFrame, business logic, user registration, account management, microservices logic, user service implementation] +description: "Detailed explanation of business logic implementation in the user service, including core functionality code implementation and best practices for user registration and management." +--- + +Like monolithic services, microservice business logic is stored in the `*/internal/logic` directory. As we're all experienced developers, I'll keep this simple and provide a basic example. + +*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/i18n/en/docusaurus-plugin-content-docs/current/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/i18n/en/docusaurus-plugin-content-docs/current/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..bbba7a6803b --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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,109 @@ +--- +title: '2.3 Protocol Files' +hide_title: true +slug: '/course/proxima-book/user-protocol' +keywords: [GoFrame, Protocol Buffers, gRPC, API definition, microservices communication, proto files, user service protocol] +description: "Introduction to Protocol Buffers protocol file definitions in the user service, including protocol design for user registration, login interfaces, and gRPC service definition best practices." +--- + +Protocol files refer to `*.proto` files. Proto is the standard communication protocol for gRPC, similar to how JSON relates to HTTP. However, it's essential to note that proto and JSON have distinct differences: proto defines both "interface" information and response/request parameters, while JSON simply stores data. + +Proto files are stored uniformly under `manifest/protobuf`, and like regular HTTP services, interface versions are managed through directory hierarchies. + +## User Registration +--- +Create a directory named `account` to manage user account-related business logic. + +*app/user/manifest/protobuf/account/v1/account.proto* +```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; +} +``` + +Let's briefly explain the proto syntax: +- **syntax**: Specifies the file's syntax version +- **package**: Defines the service namespace, similar to a package name +- **option**: Sets compilation options; `go_package` specifies the package name for generated Go code. *In GoFrame, the fixed format is `project_name + app + microservice_name + api + module_name + v1`* +- **service**: Defines remote call methods, typically RPC, specifying request and response parameters +- **message**: Defines data structures, where `string` is the data type, `username` is the field name, and the incremental numbers after the equals sign are field numbers. *The trailing comments are framework-provided parameter validations, used similarly to regular HTTP interfaces* + +Our file defines: +- Uses proto3 syntax version +- Defines package name as `account.v1` +- Sets the Go code generation package path option `go_package` to `proxima/app/user/api/account/v1` +- Defines an `Account` service with one RPC method `UserRegister` that accepts `UserRegisterReq` message and returns `UserRegisterRes` message +- Defines a message type `UserRegisterReq` with three fields: + - `username` (string type, number 1) + - `password` (string type, number 2) + - `email` (string type, number 3) +- Defines a message type `UserRegisterRes` with one field: + - `id` (integer type, number 1) + +## User Login/Query +--- +Following the same pattern, let's define the user login and query interfaces. Here's the complete file content: + +*app/user/manifest/protobuf/account/v1/account.proto* +```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; +} +``` + +This introduces two new syntax elements: +- `import "pbentity/users.proto"`: Imports another proto file. This file was generated by `gf gen pbentity` +- `pbentity.Users user`: Uses the imported data model, which is almost identical to Go structs \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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/i18n/en/docusaurus-plugin-content-docs/current/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..06e335b015f --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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,74 @@ +--- +title: "2.4 Controllers" +hide_title: true +slug: '/course/proxima-book/user-controller' +keywords: [GoFrame, controller generation, gRPC controller, protobuf generation, microservices controller, service implementation] +description: "A detailed guide on using GoFrame's code generation tools to generate gRPC controllers and implementing various functional interfaces for the user service." +--- + +While HTTP service controllers are generated using `gf gen ctrl`, microservices also have controllers, generated using `gf gen pb`. + +```bash +$ gf gen pb +``` + +The `gen pb` command requires all dependencies to be properly set up. When executed successfully, it generates several Go files. We only need to focus on the controller files; the framework maintains the rest. The subsequent development process is similar to HTTP services - calling `logic` functions. + +*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/i18n/en/docusaurus-plugin-content-docs/current/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/i18n/en/docusaurus-plugin-content-docs/current/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..b28107bd46a --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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,152 @@ +--- +title: '2.5 Service Startup' +hide_title: true +slug: '/course/proxima-book/user-run' +keywords: [GoFrame, gRPC service, service startup, microservices deployment, service registration, etcd integration] +description: "A comprehensive guide on starting and running the user microservice, including service registration, gRPC service configuration, etcd integration, and service health checks." +--- + +## Importing Controllers in CMD +--- +Like monolithic services, microservices also need to be imported in the `cmd`. The difference is that the service startup changes from `HTTP` to `gRPC`. + +*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 + }, + } +) +``` + +## Main Entry File +--- +Import the database driver and `cmd` in the main entry file. + +*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()) +} +``` + +## Configuration File +--- +*app/user/manifest/config/config.yaml* +```yaml +grpc: + name: "user" + address: ":32001" + +database: + default: + link: "mysql:root:12345678@tcp(srv.com:3306)/user" + debug: true +``` + +The `grpc` field defines two essential parameters: the microservice name and the listening port. The service name is used for service registration, and the listening port is self-explanatory. These two are mandatory; for other configuration options, refer to the [configuration template](https://goframe.org/docs/micro-service/config#%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF). + +## Starting the Service +--- +Switch to the root directory and ensure all dependencies are properly installed. + +```bash +$ cd ../../ +go mod tidy +``` + +Return to the microservice directory and start the user microservice. + +```bash +$ 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] +``` + +With this, we've completed the development of the first microservice for Proxima Notebook, which isn't much different from developing a monolithic service. + +## Testing Results +--- +> When testing gRPC in your testing tool, you'll need to use the proto protocol file. Make sure to specify the correct dependency paths. + +```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 + } + } +} diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/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.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" "b/i18n/en/docusaurus-plugin-content-docs/current/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.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" new file mode 100644 index 00000000000..3bde477246d --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/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.6.\346\234\215\345\212\241\346\263\250\345\206\214.md" @@ -0,0 +1,65 @@ +--- +title: '2.6 Service Registration' +hide_title: true +slug: '/course/proxima-book/user-etcd-register' +keywords: [GoFrame, etcd, service registration, service discovery, microservices registry, configuration management] +description: "A guide on registering the user microservice with etcd service registry, including configuration file setup, registration logic implementation, and service discovery mechanism configuration." +--- + +Next, we'll register the user microservice with `etcd` to make it available for other services to call. + +Add a configuration file with the `etcd` access address. + +*app/user/manifest/config/etcd.yaml* +```yaml +etcd: + address: "srv.com:2379" +``` + +Add the registration logic in the entry file: + +*app/user/main.go* +```go +package main + +import ( + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + + "proxima/app/user/internal/cmd" +) + +func main() { + var ctx = gctx.New() + conf, err := g.Cfg("etcd").Get(ctx, "etcd.address") + if err != nil { + panic(err) + } + + var address = conf.String() + grpcx.Resolver.Register(etcd.New(address)) + + cmd.Main.Run(ctx) +} +``` + +In fact, the key code for service registration is just one line, while the rest is code for reading the configuration file: +```go +grpcx.Resolver.Register(etcd.New(address)) +``` + +Restart the project to apply the changes. Then enter the `etcd` container and execute the following command to verify the registration: +```bash +$ etcdctl get "" --prefix --keys-only +``` + +This command shows all existing `keys` in `etcd`, where we should see our registered service: +```text +/service/default/default/user/latest/{IP}:32001 +``` + +> Service registration can be understood as similar to DNS name resolution. The service name `grpc.name` in the configuration file can be thought of as analogous to a domain name. diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241.md" new file mode 100644 index 00000000000..c1506b36b2c --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241/\347\254\254\344\272\214\347\253\240-\347\224\250\346\210\267\346\234\215\345\212\241.md" @@ -0,0 +1,12 @@ +--- +title: 'Chapter 2 - User Service' +hide_title: true +sidebar_position: 2 +slug: '/course/proxima-book/user' +keywords: [GoFrame, user service, microservices, authentication, user management, registration, login, user information] +description: "This chapter provides a detailed guide on implementing a user service using the GoFrame framework, covering core functionalities such as user registration, login, and information retrieval." +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256.md" new file mode 100644 index 00000000000..7a1ac980fb1 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256/\347\254\254\344\272\224\347\253\240-\350\277\233\344\270\200\346\255\245\347\232\204\345\255\246\344\271\240\346\226\271\345\220\221\344\270\216\345\273\272\350\256\256.md" @@ -0,0 +1,25 @@ +--- +title: Chapter 5 - Further Learning and Recommendations +hide_title: true +sidebar_position: 99 +slug: /course/proxima-book/appendix +keywords: [GoFrame, microservices learning, authentication, load balancing, service configuration, interceptors, future directions] +description: "This chapter summarizes advanced learning paths for GoFrame microservices development, covering key topics such as user authentication, multi-service calls, interceptor usage, and load balancing strategies." +--- + +First, thank you to every reader who has patiently completed this book! Are your three services running smoothly, like Proxima and its two companions? +Next, here are some additional learning directions for everyone. As the author's knowledge is limited, please forgive any shortcomings. I hope your career continues to rise and flourish! + +## Further Learning Directions +--- +- User Authentication & Authorization: Enhance user authentication in microservices and implement it in the gateway service +- Multi-Service Integration: Call multiple microservices within a single controller to complete business functions +- Server-Side Interceptors: Use the gRPC server interceptors provided by `GoFrame` +- Load Balancing: Understand microservices load balancing strategies +- Service Configuration Management: Integrate with configuration centers to provide more flexible configuration options for microservices + +## Buy Me a Coffee +--- +**Thank you to all supporters!** + +![Feature List](../assets/coffee.jpg) diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" new file mode 100644 index 00000000000..ce8417426e9 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.1.\345\211\215\347\275\256\345\207\206\345\244\207.md" @@ -0,0 +1,31 @@ +--- +title: '4.1 Prerequisites' +hide_title: true +slug: '/course/proxima-book/gateway-prepare' +keywords: [GoFrame, gateway initialization, API Gateway setup, microservices gateway, project structure] +description: "A guide on initializing the API Gateway, including creating the gateway service using GoFrame CLI and setting up the project structure." +--- + +The API Gateway is similar to a monolithic web service, with the main difference being that the concrete business logic is now implemented through microservice calls. + +## Code Initialization +--- +Execute the following command to create a service named `gateway` in the `app` directory. + +```bash +$ gf init app/gateway -a +initializing... +initialization done! +you can now run "cd app/gateway && gf run main.go" to start your journey, enjoy! +``` + +Remove the following files to start with a clean environment: +```text +app/word/api/* +app/word/internal/controller/* +app/word/internal/cmd/cmd.go +``` + +Navigate to the repository to begin development: +```bash +$ cd app/gateway \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.2.\346\216\245\345\217\243\344\270\216\346\216\247\345\210\266\345\231\250.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.2.\346\216\245\345\217\243\344\270\216\346\216\247\345\210\266\345\231\250.md" new file mode 100644 index 00000000000..3f0102ca536 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.2.\346\216\245\345\217\243\344\270\216\346\216\247\345\210\266\345\231\250.md" @@ -0,0 +1,79 @@ +--- +title: "4.2 APIs and Controllers" +hide_title: true +slug: '/course/proxima-book/gateway-controller' +keywords: [GoFrame, API design, gateway controller, HTTP endpoints, request validation, response handling] +description: "A detailed guide on designing APIs and implementing controllers for the API Gateway, including the definition and request handling logic for user login, registration, and other endpoints." +--- + +This step should be familiar to everyone, so we'll keep the explanation brief. + +## API +--- +*app/gateway/api/user/v1/user.go* +```go +package v1 + +import "github.com/gogf/gf/v2/frame/g" + +type LoginReq struct { + g.Meta `path:"users/login" method:"post" sm:"Login" tags:"User"` + Username string `json:"username" v:"required|length:3,12"` + Password string `json:"password" v:"required|length:6,16"` +} + +type LoginRes struct { + Token string `json:"token" dc:"Add Authorization: token in header for authenticated endpoints"` +} +``` + +*app/gateway/api/words/v1/words.go* +```go +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +type CreateReq struct { + g.Meta `path:"words" method:"post" sm:"Create" tags:"Word"` + Word string `json:"word" v:"required|length:1,100" dc:"Word"` + Definition string `json:"definition" v:"required|length:1,300" dc:"Word definition"` +} + +type CreateRes struct { +} + +type DetailReq struct { + g.Meta `path:"words/{id}" method:"get" sm:"Details" tags:"Word"` + Id uint `json:"id" v:"required"` +} + +type DetailRes struct { + Id uint `json:"id"` + Word string `json:"word"` + Definition string `json:"definition"` + ExampleSentence string `json:"exampleSentence"` + ChineseTranslation string `json:"chineseTranslation"` + Pronunciation string `json:"pronunciation"` + CreatedAt *gtime.Time `json:"createdAt"` + UpdatedAt *gtime.Time `json:"updatedAt"` +} +``` + +## Controller +--- +Execute the following command to generate controllers: +```bash +$ gf gen ctrl +generated: D:\project\proxima\app\gateway\api\user\user.go +generated: D:\project\proxima\app\gateway\internal\controller\user\user.go +generated: D:\project\proxima\app\gateway\internal\controller\user\user_new.go +generated: D:\project\proxima\app\gateway\internal\controller\user\user_v1_login.go +generated: D:\project\proxima\app\gateway\api\words\words.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words_new.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words_v1_create.go +generated: D:\project\proxima\app\gateway\internal\controller\words\words_v1_detail.go +done! diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.3.\345\220\257\345\212\250\350\277\220\350\241\214.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.3.\345\220\257\345\212\250\350\277\220\350\241\214.md" new file mode 100644 index 00000000000..1020ade87ee --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.3.\345\220\257\345\212\250\350\277\220\350\241\214.md" @@ -0,0 +1,132 @@ +--- +title: "4.3 Service Startup" +hide_title: true +slug: '/course/proxima-book/gateway-run' +keywords: [GoFrame, gateway configuration, service startup, HTTP server, OpenAPI, Swagger, logging setup] +description: "Detailed instructions on configuring and starting the API Gateway service, including server configuration, OpenAPI documentation, logging setup, and other key components." +--- + +Let's start by running the service before we call the microservices. + +## Configuration Files +--- +*app/gateway/manifest/config/config.yaml* +```yaml +server: + address: ":8000" + openapiPath: "/api.json" + swaggerPath: "/swagger" + logger: + path: "./log" + file: "{Y-m-d}.log" + level: "all" + stdout: true +``` + +*app/gateway/manifest/config/etcd.yaml* +```yaml +etcd: + address: "srv.com:2379" +``` + +## Command File +--- +*app/gateway/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" + "proxima/app/gateway/internal/controller/user" + "proxima/app/gateway/internal/controller/words" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "start http gateway server", + Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareHandlerResponse) + group.Group("/v1", func(group *ghttp.RouterGroup) { + group.Group("/", func(group *ghttp.RouterGroup) { + group.Bind(user.NewV1()) + group.Bind(words.NewV1()) + }) + }) + }) + s.Run() + return nil + }, + } +) +``` + +## Starting the Service +--- +*app/gateway/main.go* +```go +package main + +import ( + "github.com/gogf/gf/contrib/registry/etcd/v2" + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + "proxima/app/gateway/internal/cmd" +) + +func main() { + var ctx = gctx.New() + conf, err := g.Cfg("etcd").Get(ctx, "etcd.address") + if err != nil { + panic(err) + } + + var address = conf.String() + grpcx.Resolver.Register(etcd.New(address)) + + cmd.Main.Run(ctx) +} +``` + +```bash +$ gf run .\main.go +build running pid: 16144 +2024-12-10 15:30:30.788 [INFO] pid[16144]: http server started listening on [:8000] +2024-12-10 15:30:30.788 [INFO] {f0f10d9d4ec00f181b7a6f615f39d54b} swagger ui is serving at address: http://127.0.0.1:8000/swagger/ +2024-12-10 15:30:30.789 [INFO] {f0f10d9d4ec00f181b7a6f615f39d54b} openapi specification is serving at address: http://127.0.0.1:8000/api.json +2024-12-10 15:30:30.817 [DEBU] {f0f10d9d4ec00f181b7a6f615f39d54b} service register: &{Head: Deployment: Namespace: Name:default Version: Endpoints:192.168.10.98:8000 Metadata:map[insecure:true protocol:http]} +2024-12-10 15:30:30.900 [DEBU] {f0f10d9d4ec00f181b7a6f615f39d54b} etcd put success with key "/service/default/default/default/latest/192.168.10.98:8000", value "{"insecure":true,"protocol":"http"}", lease "7587883327293376805" + + ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | ALL | /api.json | github.com/gogf/gf/v2/net/ghttp.(*Server).openapiSpec | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | ALL | /swagger/* | github.com/gogf/gf/v2/net/ghttp.(*Server).swaggerUI | HOOK_BEFORE_SERVE +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | POST | /v1/users/login | proxima/app/gateway/internal/controller/user.(*ControllerV1).Login | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | POST | /v1/words | proxima/app/gateway/internal/controller/words.(*ControllerV1).Create | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + :8000 | GET | /v1/words/{id} | proxima/app/gateway/internal/controller/words.(*ControllerV1).Detail | +----------|--------|-----------------|----------------------------------------------------------------------|-------------------- + +``` + +Entering the `etcd` container and executing the command to view: +```bash +$ etcdctl get "" --prefix --keys-only + +/service/default/default/default/latest/{IP}:8000 +/service/default/default/word/latest/{IP}:32001 +/service/default/default/word/latest/{IP}:32002 +``` + +As we can see, all three of our services have been successfully registered. \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.4.gRPC\345\256\242\346\210\267\347\253\257.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.4.gRPC\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 00000000000..f29adf72b97 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.4.gRPC\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,140 @@ +--- +title: "4.4 Implementing the gRPC Client" +hide_title: true +slug: '/course/proxima-book/gateway-client' +keywords: [GoFrame, gRPC client, microservices communication, client configuration, service discovery, etcd integration] +description: "A detailed guide on implementing and configuring gRPC clients in the API Gateway for communication with microservices, including client initialization and service discovery functionality." +--- + +## Client Implementation +--- +The API Gateway acts as a gRPC client, while each microservice acts as a gRPC server. We'll define the gRPC client in our controller properties for later use. + +The client is defined using `grpcx.Client.MustNewGrpcClientConn(service, opts...)`. + +*app/gateway/internal/controller/user/user_new.go* +```go +package user + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/user" + v1 "proxima/app/user/api/account/v1" +) + +type ControllerV1 struct { + AccountClient v1.AccountClient +} + +func NewV1() user.IUserV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("user") + + return &ControllerV1{ + AccountClient: v1.NewAccountClient(conn), + } +} +``` + +*app/gateway/internal/controller/words/words_new.go* +```go +package words + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/words" + v1 "proxima/app/word/api/words/v1" +) + +type ControllerV1 struct { + WordsClient v1.WordsClient +} + +func NewV1() words.IWordsV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("word") + + return &ControllerV1{ + WordsClient: v1.NewWordsClient(conn), + } +} +``` + +## Interceptors +--- +Currently, our client doesn't have timeout handling, and gRPC's default timeout threshold is very high. If the gRPC server, etcd service, or network encounters issues, the API Gateway could hang indefinitely. Let's add a timeout interceptor to handle these situations. + +### Defining the Interceptor +The timeout mechanism is simple, implemented using Go's context. + +*app/gateway/utility/grpc.go* +```go +package utility + +import ( + "context" + "time" + "google.golang.org/grpc" +) + +func GrpcClientTimeout(ctx context.Context, method string, req, reply interface{}, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, +) error { + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + + err := invoker(ctx, method, req, reply, cc, opts...) + return err +} +``` + +### Using the Interceptor + +*app/gateway/internal/controller/user/user_new.go* +```go +package user + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/user" + "proxima/app/gateway/utility" + v1 "proxima/app/user/api/account/v1" +) + +type ControllerV1 struct { + AccountClient v1.AccountClient +} + +func NewV1() user.IUserV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("user", grpcx.Client.ChainUnary( + utility.GrpcClientTimeout, + )) + + return &ControllerV1{ + AccountClient: v1.NewAccountClient(conn), + } +} +``` + +*app/gateway/internal/controller/words/words_new.go* +```go +package words + +import ( + "github.com/gogf/gf/contrib/rpc/grpcx/v2" + "proxima/app/gateway/api/words" + "proxima/app/gateway/utility" + v1 "proxima/app/word/api/words/v1" +) + +type ControllerV1 struct { + WordsClient v1.WordsClient +} + +func NewV1() words.IWordsV1 { + var conn = grpcx.Client.MustNewGrpcClientConn("word", grpcx.Client.ChainUnary( + utility.GrpcClientTimeout, + )) + + return &ControllerV1{ + WordsClient: v1.NewWordsClient(conn), + } +} \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.5.\350\260\203\347\224\250\345\276\256\346\234\215\345\212\241.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.5.\350\260\203\347\224\250\345\276\256\346\234\215\345\212\241.md" new file mode 100644 index 00000000000..c267f597b00 --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/4.5.\350\260\203\347\224\250\345\276\256\346\234\215\345\212\241.md" @@ -0,0 +1,123 @@ +--- +title: "4.5 Calling Microservices" +hide_title: true +slug: '/course/proxima-book/gateway-call' +keywords: [GoFrame, microservices integration, service invocation, gRPC communication, gateway implementation, service orchestration] +description: "A detailed guide on integrating and calling microservices from the API Gateway, including implementations for both user and word services, and the encapsulation of business logic." +--- + +Next, we'll implement the microservices calls within our controllers to handle specific business logic. + +> In real-world development, complex business logic should be encapsulated in the `logic` layer, similar to monolithic web services, and then called by controllers. + +## User Service +--- +*app/gateway/internal/controller/user/user_v1_login.go* +```go +package user + +import ( + "context" + + account "proxima/app/user/api/account/v1" + + "proxima/app/gateway/api/user/v1" +) + +func (c *ControllerV1) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) { + user, err := c.AccountClient.UserLogin(ctx, &account.UserLoginReq{ + Username: req.Username, + Password: req.Password, + }) + + if err != nil { + return nil, err + } + + return &v1.LoginRes{ + Token: user.GetToken(), + }, nil +} +``` + +Let's test the gateway-to-microservice communication with a request: +```bash +$ curl -X POST http://127.0.0.1:8000/v1/users/login \ + -H "Content-Type: application/json" \ + -d '{ + "username": "oldme", + "password": "12345678" + }' + +{ + "code": 0, + "message": "", + "data": { + "token": "I am token" + } +} +``` + +Congratulations on seeing this response! You can now try implementing the other services as well. Here's the source code for reference. + +## Word Service +--- +*app/gateway/internal/controller/words/words_v1_create.go* +```go +package words + +import ( + "context" + + words "proxima/app/word/api/words/v1" + + "proxima/app/gateway/api/words/v1" +) + +func (c *ControllerV1) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) { + _, err = c.WordsClient.Create(ctx, &words.CreateReq{ + Uid: 1, + Word: req.Word, + Definition: req.Definition, + }) + + if err != nil { + return nil, err + } + + return &v1.CreateRes{}, nil +} +``` + +*app/gateway/internal/controller/words/words_v1_detail.go* +```go +package words + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gerror" + words "proxima/app/word/api/words/v1" + + "proxima/app/gateway/api/words/v1" +) + +func (c *ControllerV1) Detail(ctx context.Context, req *v1.DetailReq) (res *v1.DetailRes, err error) { + word, err := c.WordsClient.Get(ctx, &words.GetReq{ + Id: uint32(req.Id), + }) + + if err != nil { + return nil, err + } + + if word == nil { + return nil, gerror.New("word not found") + } + + return &v1.DetailRes{ + Id: uint(word.Words.Id), + Word: word.Words.Word, + Definition: word.Words.Definition, + }, nil +} \ No newline at end of file diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263.md" "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263.md" new file mode 100644 index 00000000000..f130b4eacba --- /dev/null +++ "b/i18n/en/docusaurus-plugin-content-docs/current/course/proxima-book/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263/\347\254\254\345\233\233\347\253\240-\344\270\232\345\212\241\347\275\221\345\205\263.md" @@ -0,0 +1,12 @@ +--- +title: 'Chapter 4 - API Gateway' +hide_title: true +sidebar_position: 4 +slug: '/course/proxima-book/gateway' +keywords: [GoFrame, API Gateway, microservices gateway, service orchestration, request routing, load balancing] +description: "This chapter provides a comprehensive guide on implementing an API Gateway using the GoFrame framework, covering core functionalities such as request routing, service orchestration, and load balancing." +--- + +import DocCardList from '@theme/DocCardList'; + + diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\346\225\260\346\215\256\345\272\223ORM/\346\225\260\346\215\256\345\272\223ORM.md" "b/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\346\225\260\346\215\256\345\272\223ORM/\346\225\260\346\215\256\345\272\223ORM.md" index 612b4c0b7cc..a5f684e02bd 100644 --- "a/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\346\225\260\346\215\256\345\272\223ORM/\346\225\260\346\215\256\345\272\223ORM.md" +++ "b/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\346\225\260\346\215\256\345\272\223ORM/\346\225\260\346\215\256\345\272\223ORM.md" @@ -45,12 +45,6 @@ The `GoFrame ORM` component has the following notable features: 13. Supports debugging modes, log output, `DryRun`, custom `Handler`, automatic type conversion, custom interface conversion, and other advanced features. 14. Supports query caching, soft delete, automatic time updates, model associations, database cluster configurations (software master-slave mode), and other practical features. -## Component Association - -![](/markdown/cf10ab2ff4d4b341190d5e5a47692061.png) - -`GoFrame ORM Dependencies` - ## `g.DB` vs `gdb.New` & `gdb.Instance` There are three ways to obtain database operation objects: using the `g.DB` method (recommended), using the native `gdb.New` method, and using the package's native singleton method `gdb.Instance`, with the first being the recommended usage. The differences between these three methods are as follows: diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" "b/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" index c26efad28e1..029b8e91f65 100644 --- "a/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" +++ "b/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\344\275\277\347\224\250.md" @@ -1,7 +1,7 @@ --- slug: '/docs/core/gerror-code-example' title: 'Error Code - Example' -sidebar_position: 0 +sidebar_position: 1 hide_title: true keywords: [GoFrame, GoFrame Framework, Error Handling, Error Code, Stack Information, NewCode, WrapCode, error, gerror, gcode] description: "In the GoFrame framework, the NewCode and WrapCode methods are used for error handling, allowing the creation and wrapping of error objects with custom error codes and stack information. This document provides example code based on the GoFrame framework to help developers better understand the application of error codes in programs." diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" "b/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" index 94855809f6f..f82537dd804 100644 --- "a/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" +++ "b/i18n/en/docusaurus-plugin-content-docs/current/docs/\346\240\270\345\277\203\347\273\204\344\273\266/\351\224\231\350\257\257\345\244\204\347\220\206/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\347\211\271\346\200\247/\351\224\231\350\257\257\345\244\204\347\220\206-\351\224\231\350\257\257\347\240\201\346\216\245\345\217\243.md" @@ -1,7 +1,7 @@ --- slug: '/docs/core/gerror-code-interface' title: 'Error Code - Interface' -sidebar_position: 1 +sidebar_position: 0 hide_title: true keywords: [GoFrame, GoFrame Framework, gcode, Error Handling, Error Code Interface, Interface Definition, High Scalability, Error Code Component, Custom Error Code, Interface Design] description: "The error code interface in the GoFrame framework is used for error handling, primarily describing the interface design and high scalability of the error code component gcode. By implementing the Code interface, developers can customize error codes. The framework provides a default implementation, but developers can also extend and implement their own error code logic as needed." diff --git a/i18n/en/docusaurus-plugin-content-docs/current/release/v2.0 2022-03-09.md b/i18n/en/docusaurus-plugin-content-docs/current/release/v2.0 2022-03-09.md index fe1df0ceddf..e5c89549cf9 100644 --- a/i18n/en/docusaurus-plugin-content-docs/current/release/v2.0 2022-03-09.md +++ b/i18n/en/docusaurus-plugin-content-docs/current/release/v2.0 2022-03-09.md @@ -225,10 +225,10 @@ Upgrade Guide: [Happily Upgrade from v1 to v2](../docs/其他资料/如何从v1 2. Added `CodeMessage` method for obtaining error code information of specified error. 3. Added `NewOption` method for custom-configured error object creation, dedicated to advanced players of the framework. 4. Added `HasStack` method to determine whether given error interface object has implemented (contains) stack information. -5. Changed error code from integer to interface object to achieve customizability and improve extensibility, refer to `gcode` component introduction for details: [Error Code - Example](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码使用.md) +5. Changed error code from integer to interface object to achieve customizability and improve extensibility, refer to `gcode` component introduction for details: [Error Code - Example](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码方法.md) 6. Improved usability, revised `NewCode/NewCodeSkip/WrapCode/WrapCodeSkip` methods, optional `text` input parameter, default using `Message` info of corresponding error code. 2. `/errors/gcode` -1. Added `gcode` error code component, providing highly customizable and extensible error code management, combined with `gerror` component for powerful error handling: [Error Code - Example](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码使用.md) +1. Added `gcode` error code component, providing highly customizable and extensible error code management, combined with `gerror` component for powerful error handling: [Error Code - Example](../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码方法.md) ### 5. Other Components diff --git "a/i18n/en/docusaurus-plugin-content-docs/current/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" "b/i18n/en/docusaurus-plugin-content-docs/current/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" index a8143a5bed2..bf6b48e753e 100644 --- "a/i18n/en/docusaurus-plugin-content-docs/current/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" +++ "b/i18n/en/docusaurus-plugin-content-docs/current/release/\345\216\206\345\217\262\347\211\210\346\234\254\350\256\260\345\275\225 v1.x/v1.15 2020-12-31.md" @@ -60,7 +60,7 @@ Open source is not easy, and with your understanding and support, happiness is a 3. `gerror` - Added `Newf/NewSkipf` methods for creating error objects: [Error Handling](../../docs/核心组件/错误处理/错误处理.md) - - Added support for error code features: [Error Code - Example](../../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码使用.md) + - Added support for error code features: [Error Code - Example](../../docs/核心组件/错误处理/错误处理-错误码特性/错误处理-错误码方法.md) - Enhanced unit tests. 4. `gvalid` diff --git a/i18n/en/docusaurus-theme-classic/navbar.json b/i18n/en/docusaurus-theme-classic/navbar.json index 0396bd985a7..5e8bf14eb57 100644 --- a/i18n/en/docusaurus-theme-classic/navbar.json +++ b/i18n/en/docusaurus-theme-classic/navbar.json @@ -34,5 +34,9 @@ "item.label.实战教程-星辰英语本": { "message": "Practical Tutorial - Star English Book", "description": "Navbar item with label 实战教程-星辰英语本" + }, + "item.label.微服务实战教程-比邻英语本": { + "message": "Microservice Tutorial - Proxima Book", + "description": "Navbar item with label 微服务实战教程-比邻英语本" } } diff --git a/sidebars.ts b/sidebars.ts index 321bd57000a..c4ff452b73d 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -15,12 +15,18 @@ const sidebars: SidebarsConfig = { 'course/社区教程', 'course/视频入门教程', 'course/starbook/starbook', + 'course/proxima-book/proxima-book', ], courseStarBookSidebar: [{ type: 'autogenerated', dirName: 'course/starbook' } ], + courseProximaBookSidebar: [{ + type: 'autogenerated', + dirName: 'course/proxima-book' + } + ], releaseSidebar: [{ type: 'autogenerated', dirName: 'release'