タイトル

Need for Answer

2015年6月19日金曜日

golangでHTTPのbodyを読み捨てたい!…のまき

golang書いてる人だったら、httpリクエストをひたすら繰り返すアタックツール、書きますよね!

…ということでソースをそのまま公開すると「アタックを繰り返すプログラムを公開し続けるだけのヤンチャなプログラマー」になっちゃうので、その際学んだことを書こうかな?というお話。

http接続処理のとこだけ抜粋するんですが、重要なのは「バッファ読み捨て」というところです。

なんでここが重要なの?という話なのですが、バッファを読み捨てないと「http接続が完了する前に、Client側でTCP接続を切断しちゃうから」なのです!

ちなみにバッファ読み捨ての行をコメントアウトしても、レスポンスコードは取得できたりします。

…なんでかはioutilのソースを読むといいんじゃないかなーと思います!

// HTTP接続
func httpRequest(tr *http.Transport) {

        // HTTP接続
        client := &http.Client{Transport: tr}
        r, err := client.Get(url)

        if err != nil {
                fmt.Println(err)
                return
        }

        // Body Close処理
        defer r.Body.Close()

        // バッファ読み捨て
        io.Copy(ioutil.Discard, r.Body)

        // レスポンスコード
        response := r.StatusCode
        fmt.Println(response)

}

2015年6月17日水曜日

golangでfluentdのストレステストツールを書きましたのまき

fluent-attacker-rpsという、Fluentdサーバーに負荷をかけるツールを作りました!fluentdサーバーにいっぱいデータ登録したいときに使って下さい!

…というと2行でお話終わっちゃうので、一応使い方とか説明しようかなーと。

go run fluent-attacker-rps.go -h 192.168.0.1

という感じで実行すると、こんな出力がされます。

=====================================
Insert Keys=> 3000 RPS Set => 300 Message Length => 1000
=====================================
0 loop - TaskSet Done.
300 loop - TaskSet Done.
600 loop - TaskSet Done.
900 loop - TaskSet Done.
1200 loop - TaskSet Done.
1500 loop - TaskSet Done.
1800 loop - TaskSet Done.
2100 loop - TaskSet Done.
2400 loop - TaskSet Done.
2700 loop - TaskSet Done.
3000 Task - All Job Done.
=====================================
Exec Time => 10.389512Sec Real RPS => 288.752743rps(96.250914%)
=====================================

終わりましたね!fluentdサーバーにどんなデータが来ているかといいますと、こんな感じです!
2015-06-17T11:48:19+09:00       test.tag        {"ID":"0","md5":"1cea7c2ef3b6019d774fd46a90ef127b","strings":"zrv5xl4igns0qvm6rdki5jq5qrq6wfko3gahmk41idbh5udjk6hvus3wjw1659x51fu7oe6xfc6c163s6ee7stunspm2drdcaap4mgk0khexs17chdg5hjzx4u15h7hc5ui3i256hfon1i294fh9e3dlatpfeks5o8b8lyj8lgp7oqzmsyme94qc88btpahp51ua5lbzt606p76lnxmpj7y7z0hmcb0oic8fmm0km0zg97ag4orm4scql1qvgp5b2av0o8om5k2bc5wpdutvgb22n2alf0e4tf7v42v0h9rd20ye2y3lkj5cczegurle8socvwlz9wjg7379b37w1958kpryai16yqjgxno39g7u3i1xky4oqaeuhmr2aeu0of5yhabakwgrp94oxtjlhncof5lqasudwgvmkdzh75jwia2milduhy05tf0cszg99kqoqixwpu2bsuy4vqaopu1e5x9f9ba5bsvz31a4zzt97ybs92taaalqvp4y9bsrjhc0fxwxxmdd9a89e6hldttqap08za7xmcuq8a2bvczmg1n21t0do78qeg0c79as2tpujxwyrraxijjhjsnap1kfh702jkrjlqv8v6ik6u2ys7z2b5xhh66jhtx9fnj571igv8yedtosb050omt6h62rcyaibk4gcuj6iez2g70kol9zrfiwow7o17467qrf14wwxoskavqw0t64azei1kbzsm5uncb3nspwb6tyjmyopef6yprxbvtylsx18h34rw2x3v69dh3eqv28jc1z2pnny8qgrjle5fdn2pvjuby9197f3kr3f6r031j651zb9egbqtqzgncvim02i33wbs20msvrmt1cldkwkd7qhac39e6u0hoes0e6yh9g24idnclqkwx72vbrdb5ziu425qm58j6e8rcs9nyqyevb33idwhvruhna2wo7pgkqnwgyfkv6n0laavw09t06lo1pc0s82gxexgfznroazqlc"}
2015-06-17T11:48:19+09:00       test.tag        {"strings":"j4t29f6dyejou4h320ggfxi7u4f4kzr3kzxv9u852pzqw8ft238fdqmr42eha0g826nn2vyu79n2wmjtuj17lc1n0jpbqw8bnpcie3csc34ixkiaeuy6n1xczg3uvk11h1lh3kd005xepund2bz2xpj0fha4c4h3acqxvns5x7p6wtes87trldkn8ze51qhhzrql41kx1sxcg4m6aidc406qldco4d6g6k8enbk6oblmyb6v9txdq6aigpn13thgnshi0c5yremnrw2l7bbee8jccqqvpwe035ov8oqk84xzsr2wg1kzgj4xiudg1zxajdn4q661hy4qh6f8bfqa9v4nedn2c1x5nsinkgsr3zw8t6hzdl0bxapt0aysf0qap6ak7x1k2ec09ucxz98u59yjx7b49l5tv7x8xde9bm8e503ayl51ojtmgapu5227mucnlqyr4v78xvvdta85z3160a06my9ndhmyrlboghl025cqda74dpfrbuxjguu2eamhujzeimfpcd91as0fbxj31conls96yby622i9le6cnoj54vjdstj75cg6mksguush2hg39kjs75j1nd46jjyuioxlz8cesedc00i2zg8apipzodmosp69eo0alxkxkr3xnz0u9n9hcbyan3wywiu8efr729y73afwerc8yji8ioaxivs71d3143tzk02b7toaea4j7bdz2snmqrodjtabvnwe5l90ivxl8oflae4cvkiafcvkwwi4nlh3vw98os4ftskbgvj7b6he2rtags1isqh1fszd9379upustge9gvp8pgl25gcw7xu2lre6jkx11i306gckvf5k8m6mr4iufbax80m25zyyjlac7tlty4mg24t1h7pacwo7tzf3s4vtubi7aisupo30x1w3yjlmlxiojz5ckmsp1sppntm173uqi9fw6kydxfikyzpd0iaa3sctuqigemjwq72skxsbwjhrzj8q391wjslw","ID":"1","md5":"93464e057d8babe65f77801432dc27fe"}
2015-06-17T11:48:19+09:00       test.tag        {"ID":"2","md5":"d4a115141322905aa3e21dc8ea91349c","strings":"8x0pwax2q60gbdlzbe0hq7zp5w57zt03g6qq0y6a7g9x6a9wzhjmhxx30hjtbok4wnlxgczusrenbtwbyus89ihz1czpbr75g1djbmdbfoq6c6me5xrh3w5t01bfkrccw0kiharjc1c3rb73g74d7mkvjxqk0htgkrkrr23innjjdzybn4l5q4g62d8vfpbk6reycp22jv1aavza6z8egcrh8jboewllr5dvvif8tiouqil0ncvkzpugp3s4hakzvob1b1t83lf97adwuh5177vcu54ek816vslydwqkvhseblxzpf7lwjn5te9p0t4zgoy3txhn9paswxce6rp6f8ozrldxcc0sv96o5kn1s2z6negjc12s11406z0fxpa01ybhqxtovmmiix867x8ebfoi86lcp4upo6xplwpj0kvzhaiq1o6yo2z0gh9fd6z5od0201yy6c9hk5b9eflcv72kd8wg2sric04vl43o0kv0yc3hmc4v4xcgnra0iqreipdu0fiop2je5xl8npg3gryqv27504vvu5lrtimyxhy6sqjazrrz19od263tl4mbwqlvxx0dlb7unr3563p01symis6p2pc8pkyuhgs0zb9q4lukvdqnnk2uqu42dl488drbvhs7iypxkgqxk049n5ofuj42fik7v7mmanwliw4bdei6a2q5hw1ws9kn9sl9h74dg264gtmh2ybvl0xpk0dcgxdun0ksz0aa5uoruswt49v2xs3iaf7edl3jju0u7a4cq1v8ocrb1k4hxqdninhjuxha9kdztkx2uzxiec56fpqyf66m519vnv11dff0s3rnn5f2o72amhyybl5mf5yv3bx185usi4re6rpq0sasf2dem6rb99zk9fgg2gpqrxzarm810si24bfoear4iuey45b4dqfk4p0thyypc5ii2o19a107uthif4tr0qlad44atr9xaw0nq7ehpkhawl6sy06thl6084ri13us"}

RPSとか投稿数とか調整できますので、「fluentdサーバーのスペック決めるのどーすんだろ?」とか「ログがロストしてないんだろか?」とか実験するといいんじゃないかなーと思います!

追記)念のため言っておきますが、Newで作った接続を使いまわしていないのは「大量のTCP接続来たらどうなるのかなー?」という試験のためです!アホの子をみるような目でソースを見るのはやめてね!

2015年6月12日金曜日

golangでRPSをそれなりっぽく実装するのまき

golang書いている4割位の人が悩む問題として、「RPS(Requests Per Second)をどうやって実装するか?」があると思います。…多分。

ということでこんなかんじで実装しましたよ、というお話。
ちなみにコードのほとんどはmattn氏のBig Sky :: Golang の channel の使い所 : の引用です。

以下サンプルコード。

// RPSから間隔を計算
func calcInt(x int) int64 {
        return int64(1 / float64(x) *1000 *1000)
}

func main() {
    // Channel宣言
    task := make(chan int)        // TaskQue用
    taskquit := make(chan bool)   // SetTask停止用
    workerquit := make(chan bool) // JobWorker停止用

    // WaitGroup作成
    wg := &sync.WaitGroup{}

    // Job Worker
    go func() {
    loop:
        for {
            select {
            case <-taskquit:
                workerquit <- true
                break loop
            case job := <-task:
                wg.Add(1)
                なんかの処理(job)
                wg.Done()
            }
        }
    }()

    // SetTask Worker
    go func() {

        // RPS間隔計算
        interval := calcInt(rps)
        sleepWait := time.Duration(interval) * time.Microsecond

        for i := 0; i < key; i++ {
            task <- i
            time.Sleep(sleepWait)

            // Log Print
            if i%rps == 0 {
                fmt.Printf("%d loop - TaskSet Done.\n",i)
            }
        }
        taskquit <- true
    }()

    // WaitGroup終了まで待つ
    wg.Wait()

    <-workerquit
}

2015年6月4日木曜日

ErastiCache for RedisでFailOverが発生すると何がおこるのか?のまき

これからタイトルどおりのお話をするのですが、Multi-AZ構成を取っていたとしても何がおこるか知らない人多いんじゃないでしょうか?AWSのドキュメント見ても、挙動の記載が無いのです。

結論から言うと、2パターンあります。
  1. Read Replicaがprimaryに昇格し、旧primaryはread replicaとして同期
  2. 自身の持っているイメージからデータを復元(read Replicaが使えない場合)
1から説明しますと、Multi-AZのRead Replicaが存在する場合は素直に切り替わります。これはDNSで切り替わるため、Primary Endpointに紐付いたNodeが、新しいprimaryに切り替わリます。その際、降格したread replicaはデータが空の状態で同期を開始します。

問題は2です。primaryがread replicaに降格するのですが、そのタイミングでデータをローカルにバックアップします。正確に言うと、こんなかんじの流れです。
  1. 障害検知したタイミングで、データをローカルにバックアップ
  2. 再起動
  3. データが空の状態でredisが起動
  4. 新primaryに問題ない場合はread replicaとして起動し、primaryからデータ同期
  5. 新primaryに問題がある場合はprimaryとして起動し、1.からデータを復元
何が問題か?という話なのですが、1~5の間は『read,writeがロックされた状態』になります。つまり長時間redisにアクセス出来ない状態になります。

…大変っすね!