simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

AWS:golang Web Server を binary cache DB とする2/2

前回までのあらすじ

simplestar-tech.hatenablog.com
Amazon にクレジットカードを登録してボタンをいくつか押すと、世界に向けて golang のサーバーが TLS の秘匿通信で公開され
ログ拡張でアプリの動作の内容がわかるところまで、初心者でも Web サーバーが運用できるところまで資料がまとまりました。

今回やること

サーバー内で 16x16x16x16x16x16のint配列を用意
ベーシック認証で user 名と password が一致しているかチェック
POSTリクエストのみを受け付けて、action が 1 のときのみ value で上書き
それ以外では index で指定した value を取得する

実装は次の通り

package main

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func readClientIP(r *http.Request) string {
	IPAddress := r.Header.Get("X-Real-Ip")
	if IPAddress == "" {
		IPAddress = r.Header.Get("X-Forwarded-For")
	}
	if IPAddress == "" {
		IPAddress = r.RemoteAddr
	}
	return IPAddress
}

func main() {
	cubedata := [16 * 16 * 16 * 16 * 16 * 16]int{}
	port := os.Getenv("PORT")
	if port == "" {
		port = "5000"
	}

	f, _ := os.Create("/var/app/current/golang-server.log")
	defer f.Close()
	log.SetOutput(f)

	const indexPage = "public/index.html"
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "Not Found.", http.StatusNotFound)
			return
		}
		user, pass, ok := r.BasicAuth()
		if ok == false || user != basicAuthUser || pass != basicAuthPassword {
			http.Error(w, "Not Found.", http.StatusNotFound)
			return
		}
		requestJson := new(RequestJson)
		if buf, err := ioutil.ReadAll(r.Body); err == nil {
			if err := json.Unmarshal(buf, requestJson); err != nil {
				http.Error(w, "Bad Request.", http.StatusBadRequest)
				return
			}
			log.Printf("Received message: %s from %s\n", string(buf), readClientIP(r))
		}
		if 0 > requestJson.Index || len(cubedata) <= requestJson.Index {
			http.Error(w, "Bad Request.", http.StatusBadRequest)
			return
		}

		if 1 == requestJson.Action {
			cubedata[requestJson.Index] = requestJson.Value
		}
		responseJson := ResponseJson{http.StatusOK, cubedata[requestJson.Index]}

		res, err := json.Marshal(responseJson)

		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.Write(res)
	})

	http.HandleFunc("/scheduled", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			log.Printf("Received task %s scheduled at %s\n", r.Header.Get("X-Aws-Sqsd-Taskname"), r.Header.Get("X-Aws-Sqsd-Scheduled-At"))
		}
	})

	http.HandleFunc("/hc", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.Error(w, "Not Found.", http.StatusNotFound)
		}
	})

	log.Printf("Listening on port %s\n\n", port)
	http.ListenAndServe(":"+port, nil)
}

type RequestJson struct {
	Action int `json:"action"`
	Index  int `json:"index"`
	Value  int `json:"value"`
}

type ResponseJson struct {
	Status int `json:"status"`
	Result int `json:"result"`
}

const (
	basicAuthUser     = "user"
	basicAuthPassword = "password"
)

気を付けるべきはBasic 認証の user 名とパスワードは別途違うものにします
ヘルスチェック用のパスを ALB で /hc に切り替える必要あり

クライアントからのPOST方法

ボディ設定は次の通り

f:id:simplestar_tech:20191106002802p:plain
ARCの画面

ヘッダー情報は次の通り

f:id:simplestar_tech:20191106002855p:plain
ベーシック認証で user:password を設定する

もちろん本番環境では url やドメインは本番を利用し
Basic 認証の user 名とパスワードは別途違うものにします

まとめ

本番に上記をデプロイして https アドレスに POST して、認証が通りバイナリキャッシュサーバとして機能していました。
16,777,216 キューブ情報を扱ってメモリはわずか67.2 [MB] あればいい、やりました!