使用gRPC、API网关和权限校验创建Go微服务[上]

Coding Alan 4个月前 (07-27) 582次浏览 0个评论 扫描二维码

使用Go语言创建3个微服务和1个API网关 (2022版)

我们会一起开发3个微服务和1个处理HTTP请求的API网关。HTTP请求会通过gRPC转发给这些微服务。此外还会同时处理JWT验证。

本文由两部分组成,第二部分请点击这里

我们要创建什么应用呢?

我们会一起构建一套小型电商系统,项目使用Go语言进行开发。

使用gRPC、API网关和权限校验创建Go微服务[上]

应用架构

本系列文章分成两个部分:

  1. API网关: 处理HTTP请求
  2. Auth服务: 提供注册登录及通过JWT生成Token等功能
  3. Product服务: 提供添加商品减库存查找商品等服务
  4. Order服务: 该微服务中我们只提供了创建订单功能

每个微服务都是独立的项目。

因本系列文章阅读较为耗时,所以尽可能地让这些微服务保持简洁。我们不会涉及Docker或超时问题。

学习完本部分后,读者可以使用API网关和其中一个微服务新建用户。但要求对Go和gRPC有一定的基础。

下面开干!

创建数据库

执行\l的结果如下图。可以看到我们创建了3个数据库。

Terminal PSQL

对PostgreSQL不熟悉的用户也不必担心,使用MySQL完全没有问题,考虑到国内大部分开发者更习惯使用MySQL,后续代码将使用MySQL,其实使用GORM对开发而言两者的差别并不大。

创建项目

首先我们需要创建项目。推荐创建一个存储Go项目的工作目录。入口目录读者可自行选定。

注:本文中有大量的创建目录和切换目录等命令,Windows的用户可使用Git Bash或直接在IDE中创建对应的目录

先开发API网关。

API网关

从API网关讲起可能会略显无趣,但在完成后就可以将微服务结合API网关直接进行测试了。

初始化项目

安装模块

项目结构

我们需要设置整个项目。我个人偏好在一开始就创建好所有目录和文件。有挺多目录文件,请坐稳了。

文件夹

文件

项目结构如下图所示:

Project Structure

以上使用的是VS Code,如果希望显示同样的图标,请安装Material Icon Theme插件。下面就开始写代码吧。

Protobuf文件

首先我们需要分别为3个微服务添加Protobuf文件。

认证微服务的Proto

第一个protobuf文件用于构建认证微服务。可以看到我们会定义三个端点

  • Register
  • Login
  • Validate (JSON Web Token)

pkg/auth/pb/auth.proto中加入如下代码:

订单微服务的Proto

订单微服务仅处理一项任务,创建订单。因此我们需要商品ID (稍后进行获取)、数量和用户ID。

pkg/order/pb/order.proto中添加如下代码:

商品微服务的Proto

与订单微服务通讯的准备已就绪,下面来对商品微服务做同样的操作。

Proto文件

这次会包含三个端点:

  • 创建商品
  • 查找单个商品
  • 扣除商品库存

pkg/product/pb/product.proto中添加代码如下:

Makefile

下来我们来编写Makefile。这里添加两条命令来执行其它命令。听起来像套娃,可以认为是一种快捷方式。

这样我们不用再敲下冗长的protoc pkg/**…,只要键入make proto即可。

下面就对Makefile添加代码:

现在我们就可以通过刚刚创建的proto文件来生成protobuf文件了。

控制台中的输出应该也非常简单:

生成的protobuf文件会和.proto文件放在一起,如下图:

项目结构中的Protobuf文件

环境变量

我们需要定义一些环境变量。

pkg/config/envs/dev.env中加入如下代码:

配置

在这一文件中,我们将环境文件中的数据拉给到API网关。

pkg/config/config.go中加入如下代码:

API网关的主要配置已完成。下面就需要编写三个微服务的客户端代码,服务端代码稍后编写。

Auth微服务的端点

现在我们来编写对Auth微服务的实现,该服务现在还不存在。但因为我们已定义了protobuf文件,所以知道各个微服务的请求和响应。

注册路由

如果用户希望注册账号,需要向API网关发送一条请求,我们接收到请求再转发给认证微服务。该微服务会返回响应。当然我们还没有编写这个微服务,但已经知道它接收的数据及返回的响应内容。

因此这里我们创建一个结构体RegisterRequestBody,用于绑定HTTP请求体,然后绑定所要做的gRPC请求的请求体。

pkg/auth/routes/register.go中添加如下代码:

注意! 在大部分Go文件中,需要将项目名称替换成你自己的。此处模块名位于第5行,请自行替换。

哈哈,其实大可不必担心,因为我在这里使用了本地路径~

登录路由

登录路由与注册路由非常类似。首先绑定HTTP请求体,然后发送请求体给认证微服务。

微服务的响应为JWT令牌, 这个token在后续编写的路由中会使用到。

pkg/auth/routes/login.go中添加如下代码:

认证微服务客户端

需要进行拨号来与认证微服务进行通讯。别忘了我们在config.go文件中初始化了环境文件中所存储的微服务URL。下面就来访问这一数据。

下面编写pkg/auth/client.go

认证中间件

我们需要拦截掉对商品和订单微服务的未认证请求。也就是说,对于某些路由我们只允许登录用户来访问受保护的微服务。

其实很简单,通过HTTP请求头获取到JWT令牌,然后通过认证微服务来验证这个令牌。我们此前已在auth.proto文件中定义了validate端点。

如果验证令牌正确,就让请求通过。如不正确,则抛出未认证的HTTP错误。

pkg/auth/middleware.go中编写如下代码:

初始化路由

要访问刚刚编写的路由,需要先行注册。

pkg/auth/routes.go中加入如下代码:

订单微服务的端点

现在需要对订单微服务做同样的操作。

创建订单路由

该路由也类似于上面编写的注册和登录路由。获取HTTP请求体,再将数据转发给订单微服务。

pkg/order/routes/create_order.go中加入如下代码:

订单微服务客户端

订单微服务也需要一个客户端。

pkg/order/client.go中加入如下代码:

初始化路由

需要先进行注册才能访问刚刚编写的路由。

pkg/order/routes.go中加入如下代码:

商品微服务的端点

创建商品的路由

该路由也类似前述的路由。

pkg/product/routes/create_product.go中添加如下代码:

查找单个商品的路由

这是我们首次从路由中获取参数。我们很快就会在URL中定义该参数。但这里我们先获取这个参数id,然后将字符串转换为数字,原因是我们在product.proto中定义的是整型。

pkg/product/routes/find_one.go中添加如下代码:

我们没有为product.proto中定义的DecreaseStock端点创建路由。这是因为该端点无法在API网关中直接访问。在系列文章第2部分中我们会编写调用订单微服务中该端点的代码。

商品微服务客户端

同样我们需要定义与商品微服务通讯的客户端。

pkg/product/client.go中添加如下代码:

初始化路由

同样我们需要注册刚刚创建的路由。

pkg/product/routes.go中添加如下代码:

Main文件

最后还有一件重要的事,我们需要启动应用。前面已经注册了路由,现在需要在启动应用时调用这些注册的代码。

cmd/main.go中添加如下代码:

API网关至此就大功告成了!

命令地终端中的效果如下:

命令行终端

下面我们来开发第一个微服务。

认证微服务 (go-grpc-auth-svc)

这是要编写的三个微服务中的第一个。本文已经很长了,所以会尽量保持简洁。像环境变量这样的处理方式非常类似。

 在命令行终端中进入 go-grpc-auth-svc

初始化项目

安装模块

项目结构

我们需要先配置项目。认证微服务相较API网关要更为轻量。

文件夹

文件

项目结构如下图所示:

项目结构

项目结构

Makefile

这里我们同样需要Makefile来简化输入的命令。

Makefile中加入如下代码:

Proto文件

在微服务端及API网关两端都需要proto文件。

pkg/pb/auth.proto中添加如下代码:

生成Protobuf文件

需要生成protobuf文件。

环境变量

这里需要的变量有gRPC服务端端口、数据库的URL和JWT所用到的密钥。

pkg/config/envs/dev.env中加入如下代码:

配置

我们需要为该微服务创建一个config.go文件。

pkg/config/config.go中加入如下代码:

数据模型

在这个模型中,我们在auth_svc数据库中创建一张表。此前已经在MySQL中创建过数据库auth_svc

pkg/models/auth.go中添加如下代码:

数据库连接

下面来连接数据库。

db.AutoMigrate方法会在启用应用时自动创建数据表。

pkg/db/db.go中添加如下代码:

Hash帮助函数

在该文件中,有两个函数,一个用于通过bcrypt对密码加密,另一个用于密码验证。

pkg/utils/hash.go中加入如下代码:

JWT帮助函数

这里我们根据dev.env文件中定义的密钥来生成和校验JWT令牌。

pkg/utils/jwt.go中添加如下代码:

Auth服务

这里我们编写Auth微服务的业务逻辑。在API网关中创建的认证路由会将请求转发给该文件。

pkg/services/auth.go中添加如下代码:

Main文件

最后需要启动该微服务。

我们来编写cmd/main.go

终于告一段落了,第一部分中的API网关和认证微服务就此完成。

下面运行服务。

终端中的输出如下:

终端输出

测试认证微服务和API网关

现在我们向API网关发送两个HTTP请求。要确保两个应用都进行了启动。API网关的端口是3000,认证微服务的端口是52001。

注册新用户

用户登录

这个请求的响应非常重要,因为该请求会返回JWT令牌,在第二部分其它微服务中会使用到。

响应内容如下:

整理自Kevin Vogel的文章。

后记

阅读英文原文的请注意可能会有如下错误

  1. protoc-gen-go: plugins are not supported

解决方法是替换掉Makefile中的相应内容

  1. grpc with mustEmbedUnimplemented*** method
    方法一

方法二

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址