生涯未熟

生涯未熟

プログラミングをちょこちょこと。

sqoopでhbaseへのimport時に好きな文字列をRowKeyにする

sqoopを使ってhbaseへimportする際にRowKeyを指定するには --hbase-row-key を使いますが、
通常はcolumnを一つ指定するか、カンマ区切りのリストで複数のcolumnを指定する形になります。

しかし、好きな文字列とcolumnの値を結合してをRowKeyにしたい場合はどうすればいいでしょう?

試しに以下のようなテーブルでやってみましょう。
DBはMySQLを想定しています。

テーブル名:user
カラム:
・id
・name

このテーブルからhbaseに以下のような形でimportしてみましょう。

RowKey:id_1_name_taro
CF:
・id
・name

それでは、sqoopでimportしてみます。
接続先の情報は省略しています。

sqoop import \
-- query "SELECT id, name, CONCAT('id_', id, '_name_', name) AS rowkey FROM user WHERE \$CONDITIONS"
--hbase-row-key rowkey

これでRowKeyが id_1_name_taro という形になります。
単純に CONCAT でRowKey用のカラムを作るって感じです。 あまり捻りのない感じですが、考え付くまでに時間がかかったので備忘録代わりに・・・

go1.9で追加されるpprofのLabel機能を見てみる

Golang Weekly #167 - 生涯未熟

でも紹介しましたが、go1.9からプロファイリングツールのpprofでLabel機能が使えるらしいのでサラッと見てみます。

前置き

このlabel機能は以下のissueでのproposalが採択され、

github.com

提案者のmatloob氏がPRを投げ、go1.9beta1から組み込まれることになりました。

github.com

使い方

// labelの生成
labels := pprof.Labels("key", "value")

// プロファイリングの実行
pprof.Do(ctx, labels, func(ctx context.Context) {
    // 何らかの処理
})

基本的には上記コードを使い、Label付きでプロファイルしていきます。

では、実際に簡単なWebサーバのHandler毎にCPUプロファイリングしていきます。

簡単なWebサーバをプロファイリング

今回は極々シンプルな //user をハンドリングするWebサーバを作成しました。

package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "net/http"
    _ "net/http/pprof"
    "time"
)

func main() {
    http.HandleFunc("/", rootHandler)
    http.HandleFunc("/user", userHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func rootHandler(w http.ResponseWriter, r *http.Request) {
    rootProcessing()
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    userProcessing()
}

func rootProcessing(ctx context.Context) {
    fmt.Println("Start root processing")

    rand.Seed(time.Now().UnixNano())

    for true {
        fib(rand.Intn(100))
        time.Sleep(5 * time.Second)
    }
}

func userProcessing(ctx context.Context) {
    fmt.Println("Start user processing")

    rand.Seed(time.Now().UnixNano())

    for true {
        fib(rand.Intn(100))
        time.Sleep(5 * time.Second)
    }
}

func fib(n int) int {
    if n < 2 {
        return n
    }
    return fib(n-1) + fib(n-2)
}

//user にアクセスすると、CPUがしっかり動くように計算処理が無限ループに入ります。

それでは、これにCPUプロファイリングのコードを差し込んでいきましょう。

package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "net/http"
    _ "net/http/pprof"
    "runtime/pprof"
    "time"
)

func main() {
    http.HandleFunc("/", rootHandler)
    http.HandleFunc("/user", userHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func rootHandler(w http.ResponseWriter, r *http.Request) {
    ctx := context.Background()
    rootProcessing(ctx)
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    ctx := context.Background()
    userProcessing(ctx)
}

func rootProcessing(ctx context.Context) {
    fmt.Println("Start root processing")

    labels := pprof.Labels("http-path", "/root")
    pprof.Do(ctx, labels, func(ctx context.Context) {
        rand.Seed(time.Now().UnixNano())

        for true {
            fib(rand.Intn(100))
            time.Sleep(5 * time.Second)
        }
    })
}

func userProcessing(ctx context.Context) {
    fmt.Println("Start user processing")

    labels := pprof.Labels("http-path", "/user")
    pprof.Do(ctx, labels, func(ctx context.Context) {
        rand.Seed(time.Now().UnixNano())

        for true {
            fib(rand.Intn(100))
            time.Sleep(5 * time.Second)
        }
    })
}

func fib(n int) int {
    if n < 2 {
        return n
    }
    return fib(n-1) + fib(n-2)
}

rootProcessinguserProcessing にCPUプロファイリングのコードが追加されているのが分かりますね。

これを go run main.go で実行し、 http://localhost:8080http://localhost:8080/user にアクセスしてみてください。
CPUが唸りを上げるはずです。

これで最後にCPUプロファイリング結果を見るだけです。
ターミナルで以下を実行してみてください。

go tool pprof http://localhost:8080/debug/pprof/profile
(pprof)

すると、対話状態に入りますのでおもむろに tags と入力してください。

(pprof) tags
http-path: Total 4675
      2345 (50.16%): /root
      2330 (49.84%): /user

こんな感じでCPUの状態がそれぞれのLabelごとに表示されます。
keyで設定された値にグルーピングされ、valueに対応するCPUの状態が表示されているのが分かりますね。

このような形で、各々のURLパスごとにCPUの状態を見たい場合に使ったりするのも一つの使い方です。
他にもgoroutineで並列処理を行なうような場合にも、それぞれのgoroutineに対してLabelを貼りCPUプロファイリングするのもありですね。

というわけで簡単にですが、pprofのLabel機能を紹介しました。

Golang Weekly #167

Profiler labels in Go · Go, the unwritten parts

Go 1.9の新機能としてProfiler Labelsが追加されます。

CPUプロファイラの収集データにラベルを追加することが出来るようになり、
例えば一般的なWebサーバにgoroutineでハンドラが分割された /user/top が存在する場合、
それぞれのgoroutineに対して /user /top のラベルを貼っておけば、pprofのREPLから tags を実行することで各々のCPUプロファイラ結果が見れるようになります。便利!

この辺りを記事書いている方が少ないので、改めて別記事として書こうと思います。

GitHub - tidwall/shiny: Alternative server framework for Go using I/O multiplexing

Goの新しいWAFなんですが、一番特徴的なのがI/O多重化の際にnetパッケージを用いず、直接epoll / kqueueを呼んでいるところですね。

作者のtidwall氏が挙げている特徴は以下の4つ。

  • 1つのエントリポイントと4つのイベント関数のみのシンプルなAPI
  • メモリ使用量が少ない
  • 非常に高速なシングルスレッドサポート
  • netパッケージでイベントをシミュレートすることによって、epoll / kqueueのないOSをサポート

今後どうなるか見守りたいWAFになりそうですね。

Golang Internals Part 2: Nice benefits of named return values

条件分岐によって、初期化されたオブジェクトを返すような処理の際には名前付き結果パラメータを使おうぜというお話。
ちょっとの違いで、コンパイラの生成コードがここまでスッキリするとは驚きですね。

また、deferで呼び出す関数内で名前付き結果パラメータを参照することが出来るので、

package main

import (
    "fmt"
    "strconv"
)

func main() {
    i := incrInt(1)
    fmt.Println(strconv.Itoa(i)) // print 2
}

func incrInt(i int) (result int) {
    defer func() { result++ }()
    return i
}

といった面白いコードが書けたりします。

ちょっと少ないですが、今回はここまで。

Golang Weekly #166

Golang Weeklyを読むのが最近の楽しみなしょっさんです。

Golang Weeklyは一週間ごとにメールでGo関連のブログ記事をまとめて送ってくれるサービスになります。

Golang Weekly

f:id:syossan:20170704001739p:plain

で、自分のメモがてらにオッと思った記事とかを毎週まとめていこうと思います。

For Range Semantics

for rangeの挙動に関する記事です。

Mixing Semanticsの箇所はうっかりハマりやすいところなので、気を付けたいところ。

Leveraging interfaces in golang - Part 1 - P3 Programmer

json.Unmarshalを使う際に、UnmarshalJSONを使ったら良い感じに出来たよという記事。

僕も最近UnmarshalJSONを使って良い感じに問題が解決出来たので、物凄く共感。

Stencil - Simple code templating for Go

メソッドの引数の型に合わせてキャストし、その後関数を呼び出すってのが面倒なので型に合わせてジェネレートしちゃうのはどうか?という提言とその実装に関する記事です。

確かに面白い考え方だが、「そこまでする必要あるのだろうか・・・?🤔」と思ってしまう。

goman - the missing man pages for Go binaries · Applied Go

これはビビッと来たツール。

manコマンドのように、Golangで作成されたコマンドツールのREADMEを表示するツールになります。
Usageなど使い方がREADMEに細かく書いてあるようなコマンドには使えそうですね!

という感じで簡単に気になったGolang Weeklyの記事を今後も紹介できたらなーと思います。

リモートサーバーのhbaseをクライアントから叩けるようにする

日本語での情報が無かったのでメモ代わりに書く。

リモートサーバーで構築したhbaseに対して、JavaなりGoなり何らかで作ったクライアントからテーブル操作等々できるようにする。

やることは至って簡単で、 hbase-site.xml にPropertyを追加するだけ。

<property>
    <name>hbase.zookeeper.dns.nameserver</name>
    <value>[サーバーのIP、もしくはドメイン]</value>
</property>

これで、設定した値に繋ぎに行けばOK。