はじめに
本記事は、GoとRedisでAPIを実装する (Part.1)の続きである。まだ、読んでいない方は事前に読んでいただけると幸いである。
Part.2では、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 / Reciever それぞれの処理内容としては、以下の通りである。
- Sender側
- JSON形式のデータをRedisに格納する。
- Redisで使用するキーパターンは以下の通りである
- 「ユーザID:ユーザ名」例)
A000000:Bob
- JSON形式のデータをRedisに格納する。
- Receiver側
- キーを使用し、edis内からデータを取得する。
また、今回は重複データが存在する場合、データが更新されるようにする。
2.1. main.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. interface/user.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. Sender
2.4.1. controller/sender.go
Sender側の処理について説明する。
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で使用するキーの生成はこのファイル内で簡素的に生成している。生成したkey
とpayload
を引数にdatabase.go
の関数Set(key, payload)
に渡す。
2.4.2. infrastructure/database.go (SET)
先程の、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
を使用し、更新を行う。
これでSender側の処理は終了である。
2.5 Receiver
2.5.1. controller/receiver.go
次にReceiver側の処理について説明する。ここでは、パラメータから取得した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)
}
}
}
Receiverでは、Redisに格納されているデータをクエリストリングパラメータ(URLパラメータとも言う)から、データを取得する。取得後は、json.Unmarshal()
を行い、Go Object
として構造体UserInformation
に変換する。
2.5.2. infrastructure/database.go (GET)
また先程の、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
}
以上で、Sender / Receiverの処理は終了である。
では、最後に検証を行う。
3. 検証
検証では、Talend API Testerや、Postmanと言ったツールを使用すると良い。今回は、Postmanを使用して検証を行う。
3.1. Sender
以下のURLにJSONをPOST通信で渡している。正常に渡せていれば結果として"Status": "Successfully added to 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などでDBが確認できるプラグインもあるが、その際はlocalhost:6379
を指定して確認すると良い。
3.2. Receiver
GET通信なので、URL内にパラメータとしてkey
を渡す。データが取得できれば、結果として登録したデータが返ってくる。
検証としては、以上である。
4. まとめ
今回は、2つの記事に渡り、APIの実装について記述した。今回の内容を参考にしていただければ幸いである。
また、この実装は簡素的なものであるため、懸念点なども多い。今後は、その点の改善を行っていこうと思う。