Go 微服务实战之如何实现加解密操作的微服务开发

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go 微服务实战之如何实现加解密操作的微服务开发相关的知识,希望对你有一定的参考价值。

Go

1 前言

在上一篇文章——《​​Go 微服务实战之如何使用 go-micro 写微服务应用​​》中,我们介绍了微服务的相关概念和 go-micro 框架的特点。

接下来,我们将以循序渐进的方式建立一个简易的提供加解密服务的 Go 微服务项目。首先为了创建微服务,需要前期设计几个实体:

  • 定义服务的 RPC 方法的 protocol buffer 文件
  • 具体方法实现的 handler 文件
  • 一个公开 RPC 方法的服务器 server
  • 一个可以发出 RPC 请求并获得响应结果的客户端 client

Go

2 创建 encryption.proto 文件

首先,为了将 protocol buffer 文件编译为 Go 包,需要先安装 ​​protoc​​​,下载点​​此处​​,选择你对应的系统。

本文是以 Win 进行的示例开发,下载的是 ​​protoc-21.9-win32.zip​​,解压完后添加到系统环境变量,如图所示:

Go

然后安装 ​​proto-gen-micro​​,使用如下命令:

go install github.com/go-micro/generator/cmd/protoc-gen-micro@latest

Go

接下来,创建我们的项目目录 ​​encryptService​​​ 文件夹,然后在其中创建一个 ​​proto​​​ 目录,新建一个 ​​encryption.proto​​ 文件,写入如下内容:

syntax = "proto3";
package main;
option go_package="./proto";

service Encrypter
rpc Encrypt(Request) returns (Response)
rpc Decrypt(Request) returns (Response)


message Request
string message = 1;
string key = 2;


message Response
string result = 2;

上面的文件命名了一个 ​​Encrypter​​​ 的服务,有着 ​​Request​​​ 和 ​​Response​​ 两条消息。这两条信息是用来请求加密和解密的。

  • 首行前置的文件语法是 ​​proto3​
  • 请求消息 ​​Request​​​ 有两个字段,分别为: ​​message​​​ (需要加密的信息)和 ​​key​​​(密钥)。客户端使用这些字段来发送一个 ​​plaintext/ciphertext​​ 消息
  • 响应消息 ​​Response​​​只有一个字段 ​​result​​​:它是加密/解密过程的结果。加密 ​​Encypter​​​ 服务有两个 RPC 方法:​​Encrypt​​​ 和 ​​Decypt​​,两者都是接收一个请求,然后返回一个响应。

Go

接着我们可以通过编译 ​​.proto​​ 文件来生成 Go 文件,执行如下命令:

protoc --proto_path=. --micro_out=. --go_out=. proto/encryption.proto

执行成功后会在我们的项目 ​​encryptService/proto​​ 目录下自动生成两个文件:

  • encryption.pb.go
  • encryption.pb.micro.go

文件成功生成后如图:

Go

这些自动生成的文件不需要我们手动进行修改。

3 编写 ​​encryptService​​ 微服务端

3.1 新建 utils.go 文件

接下来,我们新建一个 ​​utils.go​​ 文件,定义字符串 AES 加解密的方法,如下:

package main

import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
)

var initVector = []byte35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05

// 字符串加密函数
func EncryptString(key, text string) string

block, err := aes.NewCipher([]byte(key))
if err != nil
panic(err)


plaintext := []byte(text)
cfb := cipher.NewCFBEncrypter(block, initVector)
cipertext := make([]byte, len(plaintext))
cfb.XORKeyStream(cipertext, plaintext)
return base64.StdEncoding.EncodeToString(cipertext)


// 解密函数
func DecryptString(key, text string) string
block, err := aes.NewCipher([]byte(key))
if err != nil
panic(err)


cipertext, _ := base64.StdEncoding.DecodeString(text)
cfb := cipher.NewCFBEncrypter(block, initVector)
plaintext := make([]byte, len(cipertext))
cfb.XORKeyStream(plaintext, cipertext)
return string(plaintext)

3.2 新建 handler.go 文件

接着新建一个 ​​handler.go​​ 文件,在这个文件内为我们的服务定义业务逻辑:

  1. 首先定义一个 ​​Encrypt​​ 结构体
  2. 增加两个方法 ​​Encrypt​​​ 和 ​​Decrypt​​ 处理 RPC 请求
package main

import (
"context"

"encryptService/proto"
)

type Encrypter struct

// 将消息加密后发送请求
func (g *Encrypter) Encrypt(ctx context.Context, req *proto.Request, rsp *proto.Response) error
rsp.Result = EncryptString(req.Key, req.Message)
return nil


// 将密文解密后返回相应
func (g *Encrypter) Decrypt(ctx context.Context, req *proto.Request, rsp *proto.Response) error
rsp.Result = DecryptString(req.Key, req.Message)
return nil

如上的代码,在 ​​Encrypter​​​ 结构体中的两个方法 ​​Encrypt​​​ 和 ​​Decrypt​​ ,

func (g *Encrypter) Encrypt(ctx context.Context, req *proto.Request, rsp *proto.Response)
func (g *Encrypter) Decrypt(ctx context.Context, req *proto.Request, rsp *proto.Response)

两个方法都是接收一个 ​​context​​​ 对象、一个 RPC 请求对象、和一个 RPC 响应对象。每个方法所做的工作是调用各自的实用函数,并将响应对象返回为一个结果 ​​rsp.Result​​。

值得一提的是,​​Encrypt​​​ 加密和 ​​Decrypt​​ 解密会被映射到 protocol buffer 文件中的 RPC 方法中,如下方法:

rpc Encrypt(Request) returns (Response) 
rpc Decrypt(Request) returns (Response)

3.3 新建 main.go 文件

紧接着,我们在 ​​encryptService​​​ 根目录下,新建一个 ​​main.go​​ 文件,根据上一篇文章中对 go-micro 框架中创建微服务实例的方法,我们写入如下内容:

package main

import (
"encryptService/proto"
"fmt"

"go-micro.dev/v4"
)

func main()

// 创建一个新服务
service := micro.NewService(
micro.Name("encrypter"),
)

// 初始化
service.Init()

proto.RegisterEncrypterHandler(service.Server(),
new(Encrypter))

// 启动服务
if err := service.Run(); err != nil
fmt.Println(err)

  • ​micro.NewService​​​ 用于新建一个微服务,然后一个 ​​service​​ 对象
  • 运行 ​​service.Init()​​ 收集命令行参数
  • 通过 ​​proto.RegisterEncrypterHandler​​ 方法注册服务,这个方法是由 protocol buffer 编译器动态生成的。
  • 最后,​​service.Run​​ 启动服务

3.4 运行 encryptService 服务

我们来看一下如何正常启动整个微服务实例:

  1. 执行 ​​go mod init encrypService​

在执行这一步出现问题,比如遇到如下错误:

go: encryptClient/proto imports
go-micro.dev/v4/api: go-micro.dev/v4/api@v1.18.0: parsing go.mod:
module declares its path as: github.com/micro/go-micro
but was required as: go-micro.dev/v4/api

使用如下命令进行解决,得到 v4 版:

go get go-micro.dev/v4

成功截图如下:

Go

再来执行 ​​go mod tidy​​​ ,执行成功如下图,最后会自动生成 ​​go.mod​​​ 和 ​​go.sum​​ 文件。:

Go

使用 ​​go build .​​​ 编译整个项目,编译成功后在 Win 下会生成一个 ​​.exe​​ 的可执行文件。

编译完整个项目后的目录结构如下:

Go

最后,运行我们的 encrypService 服务,通过使用 ​​./encryptService.exe​​ 命令进行启动,成功如下:

Go

正如你从服务器终端看到的那样,go-micro 利用一个 ​​info Transport​​​ 和一个消息代理 ​​info Broker​​​ 成功启动了一个微服务。此时,我们可以通过在浏览器中访问 ​​http://127.0.0.1:58184/​​ 查看相关信息:

Go

现在,客户端可以向这些端口发送请求,但目前这些服务并不那么有用,因为我们还没有编写客户端来消费这些 API,接下来尝试建立一个 ​​encryptClient​​ 客户端,学习如何连接到这个服务器。

4 编写 ​​encryptClient​​ 客户端

同理,通过 Go Micro 框架构建客户端,通过 RPC 调用上面的服务端,接下来就是按步骤编写客户端的方法。新建一个 ​​encryptClient​​​ 目录,然后在这个目录下建立一个 ​​proto​​ 文件夹。客户端项目结构图如下:

Go

4.1 编写 proto 文件

首先,我们需要知道服务器和客户端应该同意使用相同的 protocol buffers (协议缓冲区)。同样地,Go Micro 希望服务器和客户端使用相同的 ​​.proto​​​ 文件,在上面的的例子是 ​​encryption.proto​​ 文件。

在 ​​encryptClient/proto​​​ 下创建一个和服务端相同的 ​​encryption.proto​​ 文件:

syntax = "proto3";
package main;
option go_package="./proto";

service Encrypter
rpc Encrypt(Request) returns (Response)
rpc Decrypt(Request) returns (Response)


message Request
string message = 1;
string key = 2;


message Response
string result = 2;

类似地,使用 ​​protoc -I=. --micro_out=. --go_out=. proto/encryption.proto​​ 命令执行生成 Go 文件,如图:

Go

4.2 编写 main.go

package main

import (
"context"
"encryptClient/proto"
"fmt"

"go-micro.dev/v4"
)

func main()

// 创建新服务
service := micro.NewService(micro.Name("encrypter.client"))

// 初始化客户端,解析命令行参数
service.Init()

// 创建新的加密服务实例
encrypter := proto.NewEncrypterService("encrypter", service.Client())

// 调用 encrypter 加密服务
rsp, err := encrypter.Encrypt(context.TODO(), &proto.Request
Message: "Hello world",
Key: "111023043350789514532147",
)

if err != nil
fmt.Println(err)


// 打印响应
fmt.Println(rsp.Result)

// 调用解密 decrypter 服务
rsp, err = encrypter.Decrypt(context.TODO(), &proto.Request
Message: rsp.Result,
Key: "111023043350789514532147",
)

if err != nil
fmt.Println(err)


// 打印解密结果
fmt.Println(rsp.Result)
  • ​service := micro.NewService(micro.Name("encrypter.client")​​ 新建服务实例
  • 调用加密服务时,传入 ​​"Hello world"​​​ 文本和一个密钥 ​​"111023043350789514532147"​
  • ​fmt.Println(rsp.Result)​​​,最后在终端打印 ​​rsp.Result​
  • 调用解密服务时,传入加密的结果 ​​rsp.Result​​​ 和同一个密钥 ​​"111023043350789514532147"​
  • 然后打印解密结果 ​​fmt.Println(rsp.Result)​

编写完成后,执行 ​​go mod init encryptClient​​,如图:

Go

接着,使用 ​​go mod tidy​​​ ,自动生成 ​​go.sum​​ 文件。

然后执行编译 ​​go build .​​​ ,生成 ​​encryptClient.exe​​ 文件。

最后执行客户端打印,终端输出 Hello world 的 AES 加密文本 ​​8rqECLu6rQTfkCM=​​​ 和解密后的明文 ​​Hello world​​:

$ ./encryptClient.exe 
8rqECLu6rQTfkCM=
Hello world

执行过程,如图所示:

Go

这个过程证明了我们的加解密微服务的 RPC 调用时成功的,不过也能看到通过使用 Go Micro 框架,我们能通过几行代码,就创建了微服务和客户端。

5 总结

本文通过实现加解密操作展示了一个微服务应用的开发过程。通过编写服务端,成功运行了一个微服务实例,该服务能够通过加密请求得到一个加密后的密文,通过解密请求将消息进行解密,并返回明文结果。然后通过编写客户端向服务端进行 RPC 调用,成功将 Hello world 字符串进行加密并打印出密文和明文的结果。

这个过程充分展示了 Go Micro 框架的便利性,至于 Go Mirco 框架还有更多的知识等着大家学习。希望本文能起到抛砖引玉的效果,让更多看到文章的人加入学习和微服务的开发当中。

希望本文能对你有所帮助,如果喜欢本文,可以点个赞或关注。

这里是宇宙之一粟,下一篇文章见!

宇宙古今无有穷期,一生不过须臾,当思奋争。

参考链接:

以上是关于Go 微服务实战之如何实现加解密操作的微服务开发的主要内容,如果未能解决你的问题,请参考以下文章

微服务实战之 Go gRPC 调试工具

重新理解微服务之它还那么纯粹吗?

.Net微服务实战之可观测性

.Net微服务实战之可观测性

2.Go语言高并发与微服务实战 --- 微服务概述

2.Go语言高并发与微服务实战 --- 微服务概述