チラシの裏の書き置き

技術的な話をするブログのタイトルじゃない気がする

Go言語入門記(その1?)

とある企画を細々と進めていて、その中で「あるデータを定期的にCronで取ってKVSに突っ込む」みたいなスクリプトを作ることになっていました。
で、パパッとPHPで書こうかと思っていたのですが、丁度Go言語の入門本を入手したところだったので、せっかくなのでGoで書いてみる事に。

改訂2版 基礎からわかる Go言語

改訂2版 基礎からわかる Go言語

 

その過程でいろいろ発見とか躓きがあったので、そのメモです。その1ってなってるけど、続くかどうかは不明。

やりたいこと

  • GETでAPIにアクセス
  • JSONで帰ってくるので、キーのみ抽出
  • 上のキーと最初に取得したJSONを値とした組み合わせでKVS(redis)に突っ込む

今のところ

それぞれはまだ連結してなくて、それぞれ実現したい事を単独で試している感じ。
エラー処理は何も考えてなくて、ブランク識別子( _ )で見なかったことにしている。

GETによるアクセス

これは簡単でした。
APIはOAuthとかではなくヘッダにトークンを入れて認証する形式なので、Header.Setでセットする感じになりました。

package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
  var req, _ = http.NewRequest("GET", "エンドポイント", nil)
  req.Header.Set("X-Token", "トークン")

  var client = new(http.Client)
  var resp, _ = client.Do(req)
  defer resp.Body.Close()

  var byteArray, _ = ioutil.ReadAll(resp.Body)
  fmt.Println(string(byteArray))
}

一見難しそうだけど、実際に書いてみるとスラスラと書けるので非常に良い。

redisにSET/GETする

さすがに標準対応していないので、redigoを使用。レリゴー。
redisはVagrantにで構築した仮想環境内にあるので、別途アドレスを指定。

package main

import (
  "github.com/garyburd/redigo/redis"
  "fmt"
)

func main() {
  var conn, _ = redis.Dial("tcp", "192.168.33.100:6379")
  defer conn.Close()

  conn.Do("SET", "123", "aaatest")

  var str, _ = redis.String(conn.Do("GET", "123"))

  fmt.Printf("%#v\n", str)
}

ライブラリのおかげが、これも書きやすかった。

JSONのパース

一番困ったのがここ。
そもそもGoにおけるJSONの扱いは、わざわざJSONに対応した構造体を宣言しないといけないので面倒そうだなという感じでした。

考えることは皆同じのようで、いろいろ調べてみるとgo-simplejsonといういい感じのパッケージがありました。
基本的にはこれで良さそうなのですが、今回取得するのは次のような・・

[
  {
    "message_id": 1,
    "account": {
      "account_id": 123,
      "name": "Bob"
    },
    "body": "Hi there!"
  },
  {
    "message_id": 2,
    "account": {
      "account_id": 334,
      "name": "Mike"
    },
    "body": "Hello!"
  }
]

最初からキーなしの配列になっているJSONで、どうやらgo-simplejsonはこの形式が上手く取れないようでした。 (もしかしたら取れるのかもしれない)

なので、大人しくencoding/jsonjson.Unmarshal()を使うことにしました。

Unmarshalもそのままだと上のようなJSONに対応してないので、ここを参考にパースしました。

package main

import (
    "encoding/json"
    "fmt"
)

const data = `
[
  {
    "message_id": 1,
    "account": {
      "account_id": 123,
      "name": "Bob"
    },
    "body": "Hi there!"
  },
  {
    "message_id": 2,
    "account": {
      "account_id": 334,
      "name": "Mike"
    },
    "body": "Hello!"
  }
]
`

type accountData struct {
  AccountID       int64 `json:"account_id"`
  Name            string `json:"name"`
}

type messageData struct {
  MessageId   int64 `json:"message_id"`
  Accont      accountData `json:"account"`
  Body        string `json:"body"`
}

type JsonData struct {
  Message []messageData
}

func main() {
    messageKey := make([]messageData, 0)
  json.Unmarshal([]byte(data), &messageKey)

  for _, value := range messageKey {
    fmt.Println(value.MessageId)
  }
}

JSON渡すだけでいい感じにしてくれるとシンプルになって有り難いのですが、まぁあまり深く考えないようにします。
郷に入っては郷に従えとはよく言ったものです。

これから

とりあえずは各処理をつなげて想定したものを形にするのが第一目標ということで。
最終的にはcronに頼らず、タイマー処理とかも内部で完結させられればという感じです。