在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. 发送者

スクリーンショット 2020-02-01 17.02.03.png

我正在使用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. 接收者

スクリーンショット 2020-02-01 17.18.26.png

由于是GET通信,因此在URL中作为参数传递key。如果可以获取数据,返回的结果将是已注册的数据。

作为验证,就是以上的内容。

概括

希望您能够参考本次共分为两篇文章的实现API的描述。如果您能够参考本次讨论的内容,我们将不胜感激。

此外,由于该实施方案过于简化,存在许多令人担忧的方面。未来,我们将致力于改善这些问题。

继续补充

サンプルコード

ホットリロードをFreshから、Airに変更

广告
将在 10 秒后关闭
bannerAds