在Docker环境中使用Go和Redis实现API(第2部分)
首先
希望还没有阅读过的读者能事先阅读本文,并将其视为”在Go和Redis中实现API(第一部分)”的延续。
在第二部分中,我们将从API的实现开始进行验证。
首先,对于这次的实施,执行环境如下所示。
1. 执行环境
-
- macOS Catalina Ver.10.15.2
-
- Docker version 19.03.5, build 633a0ea
-
- docker-compose version 1.24.1, build 4667896b
-
- golang 1.13.6
- Redis 5.0.7
1.1. 文件结构
sample_project
├── docker
│ ├── api
│ │ └── Dockerfile-api //API用
│ └── database
│ └── Dockerfile-redis //DB用
├── docker-compose.yml
└── src
└── app
├── controller
│ ├── sender.go
│ └── receiver.go
├── infrastructure
│ └── database.go
├── interface
│ └── user.go
└── main.go
2. API的实现
本次实施将使用JSON格式的数据进行处理。
{
// ユーザID
"ID" : "A000000",
// ユーザ名
"Name" : "Bob",
// 年齢
"Age" : 18,
}
发送者/接收者的处理内容如下所述。
-
- Sender側
JSON形式のデータをRedisに格納する。
Redisで使用するキーパターンは以下の通りである
「ユーザID:ユーザ名」例)A000000:Bob
Receiver側
キーを使用し、edis内からデータを取得する。
另外,如果存在重复数据,本次将会更新数据。
2.1. 主要.go
首先,我們解釋一下 main.go 檔案。在這裡,我們只做 HTTP 通信。我們使用 gin.Default() 建立一個帶有預設中間件的路由器,並在每個終點指定處理的操作。
package main
import (
"app/controller"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// Sender
router.POST("/sender", controller.Send())
// Receiver
router.GET("/receiver", controller.Receiver())
// PORT環境変数が未指定の場合、デフォルトで8080で待受する
router.Run()
}
为了更易懂,这次将终端点设定为/sender和/receiver。
2.2. 接口/用户.go
为了接收通过JSON传递的数据,需要先准备一个结构体。
package _interface
type UserInformation struct {
// ユーザID
ID string `json:"id"`
// ユーザ名
Name string `json:"name"`
// 年齢
Age int `json:"age"`
}
2.3. 连接/断开与 Redis 的操作
为了避免在不同地方重复编写与Redis连接相关的代码且可读性不佳,对连接过程进行实例化,以便将操作集中处理。
import (
"github.com/garyburd/redigo/redis"
"os"
)
type Redis struct {
connection redis.Conn
}
// Redisへの接続
func NewRedis() *Redis {
// IPポートの設定
const ipPort = "redis:6379"
// redisに接続する
c, err := redis.Dial("tcp", ipPort)
if err != nil {
panic(err)
}
// 接続情報をConnインタフェースのconnectionに保存
r := &Redis{
connection: c,
}
return r
}
// Redisからの切断
func (r *Redis) CloseRedis() {
// redisとの通信を切断する
_ = r.connection.Close()
}
端口号默认设置为6379。
Conn接口是用于使用Redis的主要接口。
此外,还将断开连接的过程进行了方法封装。
2.4. 发件人
2.4.1. 控制器/发送者.go
解释发送方的处理。
package controller
import (
"app/infrastructure"
"app/interface"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func Send() gin.HandlerFunc {
return func(context *gin.Context) {
// Redisに接続する
redis := infrastructure.NewRedis()
// Send()処理終了後にRedisとの接続を切断する
defer redis.CloseRedis()
// requestInformationをUserInformationで初期化する
requestInformation := _interface.UserInformation{}
// 構造体をBINDする
err := context.Bind(&requestInformation)
if err != nil{
context.JSON(http.StatusBadRequest, gin.H{"Status": "BadRequest"})
}
// Redisで使用するキーの作成
key := requestInformation.ID + ":" + requestInformation.Name
// 作成した構造体requestInformationをJSONに変換する
payload, err := json.Marshal(requestInformation)
if err != nil {
fmt.Println("JSON Marshal Error : ", err)
return
}
// key, payloadを引数にRedisに追加する
if err := redis.Set(key, payload); err != nil {
fmt.Println("Failed to store data in Redis. ", err)
} else {
context.JSON(http.StatusOK, gin.H{"Status": "Successfully added to redis. "})
}
}
}
一度获取的数据将存储在结构体requestInformation中,然后使用json.Marshal()将其转换为JSON字符串。本次在此文件中简单地生成了用于Redis操作的键。将生成的键和有效载荷作为参数传递给database.go文件中的Set(key, payload)函数。
2.4.2. 基础设施/数据库.go(设置)
在之前的database.go文件中,添加代码以将数据存入Redis数据库。
import (
"fmt"
"github.com/garyburd/redigo/redis"
"os"
)
// ...省略
// Redisへのデータ追加
func (r *Redis) Set(key string, payload []byte) error {
// 生成したキーが既に存在するかチェックする
if r.keyExist(key) == true {
fmt.Println("Delete the key because it was already registered in redis.")
fmt.Println("Update an existing key.")
// 存在する場合、データを更新する
r.update(key, payload)
} else {
// キーをRedisに追加する
if _, err := r.connection.Do("SET", key, payload); err != nil {
fmt.Println("infrastructure/database/Set() : ", err)
os.Exit(1)
return err
}
}
return nil
}
// キーチェック
func (r *Redis) keyExist(key string) bool {
// キーが既にRedis内に存在するかチェックする
result, err := redis.Bool(r.connection.Do("EXISTS", key))
if err != nil {
fmt.Println("infrastructure/database/keyExist() : ", err)
}
return result
}
// データの更新
func (r *Redis) update(key string, payload []byte) {
// キーから値を取得後、新たなデータを登録する
_, err := r.connection.Do("GETSET", key, payload)
if err != nil {
fmt.Println("infrastructure/database/update() : ", err)
}
}
本次添加了删除重复数据的处理,因此先使用keyExist()函数检查Redis内是否存在键。如果存在,则通过update()函数从现有键中获取数据并更新为新数据。Redis中不存在UPDATE操作,可以使用GETSET来代替并进行更新。
这样一来,发送方的处理就结束了。
2.5 接收器
2.5.1. 控制器/接收器.go
接下来将对接收端的处理进行说明。在这里,我们使用从参数中获取的key,并从redis中获取数据。
package controller
import (
"app/infrastructure"
_interface "app/interface"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func Receive() gin.HandlerFunc {
return func(context *gin.Context) {
// Redisに接続
redis := infrastructure.NewRedis()
// Receive()処理終了後にRedisとの接続を切断する
defer redis.CloseRedis()
// クエリストリングパラメーターを取得
key := context.Query("key")
// responseInformationをUserInformationで初期化する
responseInformation := _interface.UserInformation{}
// Redisからデータを取得する
if payload, err := redis.Get(key); err != nil {
fmt.Println("Failed to get data from Redis. :", err)
} else {
// Redisから取得したpayloadをGo Object(構造体)に変換する
if err := json.Unmarshal(payload, &responseInformation); err != nil {
fmt.Println("Could not Unmarshal the retrieved json. :", err)
}
context.JSON(http.StatusOK, responseInformation)
}
}
}
在接收器上,从Redis中获取存储在查询字符串参数(也称为URL参数)中的数据。然后使用json.Unmarshal()将其转换为Go对象,即UserInformation结构体。
2.5.2. 基础设施/数据库.go(获取)
另外,在之前的database.go文件中添加GET处理的代码。
import (
"fmt"
"github.com/garyburd/redigo/redis"
"os"
)
// ...省略
func (r *Redis) Get(key string) ([]byte, error) {
// キーを使用し、Redisからデータを追加する
payload, err := redis.Bytes(r.connection.Do("GET", key))
if err != nil {
fmt.Println("infrastructure/database/Set() : ", err)
return payload, err
}
return payload, err
}
以上,发件人/收件人的处理已经完成。
在最后进行验证。
3. 验证
进行验证时,可以使用Talend API Tester或Postman等工具。本次我们将使用Postman进行验证。
3.1. 发送者

我正在使用POST通信传递JSON到以下网址。如果成功传递,将返回结果:”Status”: “成功添加到redis。”
URL
http://localhost:8080/sample_project/sender
結果
{
"Status": "Successfully added to redis. "
}
另外,我们需要确认Redis中的数据是否已正确注册。请进入sample_project/redis:0.1.0容器并执行以下命令进行确认。
$ docker exec -it [CONTAINER ID] sh
/data # redis-cli
127.0.0.1:6379> GET key(自分で設定したキー)
"{\"id\":\"sample001\",\"name\":\"test001\",\"age\":99}"
在IDE或其他工具中,也有一些可以查看数据库的插件,但在这种情况下,最好指定localhost:6379进行确认。
3.2. 接收者

由于是GET通信,因此在URL中作为参数传递key。如果可以获取数据,返回的结果将是已注册的数据。
作为验证,就是以上的内容。
概括
希望您能够参考本次共分为两篇文章的实现API的描述。如果您能够参考本次讨论的内容,我们将不胜感激。
此外,由于该实施方案过于简化,存在许多令人担忧的方面。未来,我们将致力于改善这些问题。
继续补充
サンプルコード
ホットリロードをFreshから、Airに変更