본문 바로가기

MSA - Docker Swarm

[Go MSA with Docker Swarm] 03-Embdded_DB

Part3에서는 Accountservice가 더 실용성 있게 만들겠습니다

  • Accout Domain 정의
  • Key-Value 저장공간 임베딩
  • /accounts/:accountId PATH로 오는 HTTP 요청에 대한 json 반환

Account 도메인 정의 - /model/account.go

package model

type Account struct {
        Id string `json:"id"`
        Name string  `json:"name"`
}

Key-Value 저장소

go get github.com/boltdb/bolt

/dbclient/boltclient.go

package dbclient

import (
    "encoding/json"
    "fmt"
    "github.com/boltdb/bolt"
    "github.com/callistaenterprise/goblog/accountservice/model"
    "log"
    "strconv"
)

type IBoltClient interface {
    OpenBoltDb()
    QueryAccount(accountId string) (model.Account, error)
    Seed()
}

// Real implementation
type BoltClient struct {
    boltDB *bolt.DB
}

// Start seeding accounts
func (bc *BoltClient) Seed() {
    bc.initializeBucket()
    bc.seedAccounts()
}

// Creates an "AccountBucket" in our BoltDB. It will overwrite any existing bucket of the same name.
func (bc *BoltClient) initializeBucket() {
    bc.boltDB.Update(func(tx *bolt.Tx) error {
        _, err := tx.CreateBucket([]byte("AccountBucket"))
        if err != nil {
            return fmt.Errorf("create bucket failed: %s", err)
        }
        return nil
    })
}

// Seed (n) make-believe account objects into the AcountBucket bucket.
func (bc *BoltClient) seedAccounts() {

    total := 100
    for i := 0; i < total; i++ {

        // Generate a key 10000 or larger
        key := strconv.Itoa(10000 + i)

        // Create an instance of our Account struct
        acc := model.Account{
            Id: key,
            Name: "Person_" + strconv.Itoa(i),
        }

        // Serialize the struct to JSON
        jsonBytes, _ := json.Marshal(acc)

        // Write the data to the AccountBucket
        bc.boltDB.Update(func(tx *bolt.Tx) error {
            b := tx.Bucket([]byte("AccountBucket"))
            err := b.Put([]byte(key), jsonBytes)
            return err
        })
    }
    fmt.Printf("Seeded %v fake accounts...\n", total)
}

func (bc *BoltClient) OpenBoltDb() {
    var err error
    bc.boltDB, err = bolt.Open("accounts.db", 0600, nil)
    if err != nil {
        log.Fatal(err)
    }
}

func (bc *BoltClient) QueryAccount(accountId string) (model.Account, error) {
    // Allocate an empty Account instance we'll let json.Unmarhal populate for us in a bit.
    account := model.Account{}

    // Read an object from the bucket using boltDB.View
    err := bc.boltDB.View(func(tx *bolt.Tx) error {
        // Read the bucket from the DB
        b := tx.Bucket([]byte("AccountBucket"))

        // Read the value identified by our accountId supplied as []byte
        accountBytes := b.Get([]byte(accountId))
        if accountBytes == nil {
            return fmt.Errorf("No account found for " + accountId)
        }
        // Unmarshal the returned bytes into the account struct we created at
        // the top of the function
        json.Unmarshal(accountBytes, &account)

        // Return nil to indicate nothing went wrong, e.g no error
        return nil
    })
    // If there were an error, return the error
    if err != nil {
        return model.Account{}, err
    }
    // Return the Account struct and nil as error.
    return account, nil
}

/service/handlers.go

package service

import (
    "encoding/json"
    "github.com/callistaenterprise/goblog/accountservice/dbclient"
    "github.com/gorilla/mux"
    "net/http"
    "strconv"
)

var DBClient dbclient.IBoltClient

func GetAccount(w http.ResponseWriter, r *http.Request) {

    // Read the 'accountId' path parameter from the mux map
    var accountId = mux.Vars(r)["accountId"]

    // Read the account struct BoltDB
    account, err := DBClient.QueryAccount(accountId)

    // If err, return a 404
    if err != nil {
        w.WriteHeader(http.StatusNotFound)
        return
    }

    // If found, marshal into JSON, write headers and content
    data, _ := json.Marshal(account)
    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Content-Length", strconv.Itoa(len(data)))
    w.WriteHeader(http.StatusOK)
    w.Write(data)
}

main.go

func main() {
        fmt.Printf("Starting %v\n", appName)
        initializeBoltClient()                 // NEW
        service.StartWebServer("6767")
}

// Creates instance and calls the OpenBoltDb and Seed funcs
func initializeBoltClient() {
        service.DBClient = &dbclient.BoltClient{}
        service.DBClient.OpenBoltDb()
        service.DBClient.Seed()
}
> go run *.go
Starting accountservice
Seeded 100 fake accounts...
2017/01/31 16:30:59 Starting HTTP service at 6767

> curl http://localhost:6767/accounts/10000
{"id":"10000","name":"Person_0"}