uokadaの見逃し三振は嫌いです

ここで述べられていることは私の個人的な意見に基づくものであり、私が所属する組織には一切の関係はありません。

navigator.sendBeaconを動かしてみた!!

What is navigator.sendBeacon?

navigator.sendBeaconってなんのこっちゃって感じなのでこれを読むと良い。

Beacon
日本語版: Beacon (日本語訳)

何がうれしいの!?って感じですが、例えばリンクをクリックした時にXMLHttpRequestでサーバーに情報を送っているようなケースで
サーバーのレスポンスが悪かったりするとサーバーにデータを送る前に遷移が完了してしまって取りこぼしてしまって正確なデータの収集が出来ないようなケースがあります。 また、サーバーからのレスポンスを待って遷移させるようなケースではサーバー側のレスポンスが悪いとページ遷移の妨げになりユーザーエクスペリエンスの低下につながります。

これをBeacon APIを使うことでユーザー・アクションを妨げることなくブラウザのバックグラウンドで処理を実行して確実にサーバーにデータを送れるようになります。
そして、送ったデータは広告なりアクセス解析なりなんなりと利用すればいいと思います。

サーバーを書いてみる

まずはサーバーを書いてみます。

package main

import (
    "encoding/base64"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    apachelog "github.com/gorilla/handlers"
    "os"
)

const (
    body string = `
<html>
<script>
  // emit non-blocking beacon to record state update
  function reportEvent(eventData) {
    navigator.sendBeacon('/collector', eventData);
  }

  // emit non-blocking beacon to record click on a link
  function reportClick(url) {
    var clickData = JSON.stringify({
      clickTo: url,
      clickAt: performance.now()
    });
    navigator.sendBeacon('/collector', clickData);
  }

  // emit non-blocking beacon when page transitions to background state
  document.addEventListener('visibilitychange', function() {
    if (document.visiblityState === 'hidden') {
      var analyticsData = buildSessionReport();
      navigator.sendBeacon('/collector', analyticsData);
    }
  });
</script>

<body>
 <a href='http://www.w3.org/' onclick='reportClick(this)'>
 <button onclick="reportEvent('button event!')">Click me</button>
</body>
</html>
`
    base64GifPixel = "R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="
)

type BaseHandler struct{}

func (bh *BaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, body)
}

type BeaconHandler struct {
    BeaconImage []byte
    RequireTLS  bool
}

func (bh *BeaconHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    log.Println("send beacon")
    //if bh.RequireTLS  r.TLS == nil {
    // log.Printf("WARNING: Please Requst with https.\n")
    //}
    if s, err := ioutil.ReadAll(r.Body); err == nil {
        log.Printf("DEBUG: %#v\n", string(s))
    } else {
        log.Printf("DEBUG: Error %#v\n", err)
    }
    output := string(bh.BeaconImage)
    w.Header().Set("Content-Type", "image/gif")
    //w.Header().Set("Content-Length", string(len(output)))
    io.WriteString(w, output)
    return
}

func main() {
    port := 8000
    mux := http.NewServeMux()

    // set demo page handler
    bh := &BaseHandler{}
    mux.Handle("/hey", bh)

    // Convert image && set handdler
    output, _ := base64.StdEncoding.DecodeString(base64GifPixel)
    beacon := &BeaconHandler{
        BeaconImage: output,
    }
    mux.Handle("/collector",
        apachelog.CombinedLoggingHandler(os.Stderr, beacon))

    // Extra
    mux.Handle("/", http.RedirectHandler("/hey", http.StatusFound))


    fmt.Printf("Listen: %d\n", port)
    addr := fmt.Sprintf(":%d", port)
    srv := http.Server{
        Addr:     addr,
        Handler:  mux,
        ErrorLog: nil,
        //ReadTimeout:  10 * time.Second,
        //WriteTimeout: 10 * time.Second,
    }
    err := srv.ListenAndServe()
    if err != nil {
        log.Fatal(err)
    }
}

検証用のサーバースクリプトを用意したのでとりあえず、サーバーを起動してみよう。

% go run server.go
Listen: 8000

ブラウザからアクセスしてみる

navigator.sendBeaconが実装されているブラウザはあまり多くない。
caniuseで確認してみるとFirefoxChromeには実装されているがそれ以外のサポート状況はあまりよろしくない。
モバイル界隈も最新のAndroidのブラウザはサポートされているがiOS safariがサポートされていない状況となっている。
個人的にiOS safariがサポートされていないのは痛い状況で日本のWEB界隈だとiOS:Androidが7:3ぐらいなので7に実装されてないとnavigator.sendBeaconを利用する側のモチベーションに影響するんじゃないかと思う。
また、Can I useのページにも日本におけるサポートブラウザの比率が掲載されているがまだ 38.87% ということで半分にも満たない。
普及にはiOS Safariで実装がされることが必須のように思う。

Can I use... Support tables for HTML5, CSS3, etc

自分はFirefox 42からこのURLにアクセス。
アクセスしたらボタンを押して遷移してW3Cのページに移動しよう。
移動したら、コンソールに戻ってみたください。 下のようなログが落ちてるはずです。

% go run server.go
Listen: 8000

2015/12/13 02:34:45 send beacon
2015/12/13 02:34:45 DEBUG: "event!"
2015/12/13 02:34:45 send beacon
2015/12/13 02:34:45 DEBUG: "{\"clickTo\":{},\"clickAt\":2601.465}"

ここには詳細は書いてませんが仕様にはPOSTでリクエストすることとなってますので比較的大きなデータでも受け渡すことが可能です。

Beacon-Age ヘッダフィールドというものが定義されています。 これはユーザーアクションからサーバーに送るまで非同期で処理されるためアクションを起こした正確な時間をサーバー側で取得するためのヘッダーとなっています。 ただ、tcpdump使ってhttpの通信みた限りChrome,Firefoxともに実装されていないので頭の片隅に入ってればいいんじゃないかなと。

Link

Goで一定時間だけ立ち上がるサーバーを書いた

一定時間だけ立ち上がって自動的に終了するサーバーをGoで書いてみた。
あんまり用途はないのかもしれないが、例えばcronで毎日0:00から5分だけ立ち上がっていてそこにアクセスしてサーバーメトリクスを収集するみたいな使い方が出来るかもしれない。

いちおうコードの解説。
main関数のところだけコードをの詳細を書いてみる。
まずはじめに、runServer() でサーバーを立ち上げる。
次に、 time.Tick t で一定時間経過後にシグナルを送るようなselectループを作成する。
time.Tick et はただのデバッグ用のコードなので削除しても構わない。
最後に、 チャネル ch で入力を待って終了する。

func main() {
    go runServer()

    t := time.Tick(time.Duration(duration) * time.Second)
    et := time.Tick(time.Duration(1) * time.Second)
    go func() {
        for {
            select {
            case <-t:
                ch <- true
            case <-et:
                // debug code
                fmt.Printf("[%d] server running!\n", time.Now().Unix())
            }
        }
    }()
    _ = <-ch
    fmt.Println("Finished.")
}

試しに、3秒で終了するように動かしてた結果がこんな感じ。
きっちり、3回デバッグ用のコードが出力されて終了している。

% go run hey_server.go -duration=3
[1446923055] server running!
[1446923056] server running!
[1446923057] server running!
Finished.

以下、コード全体。

package main

import (
    "flag"
    "fmt"
    "log"
    "net/http"
    "time"
)

type BaseHandler struct {
    Name string
}

func (bh *BaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if bh.Name != "" {
        fmt.Fprint(w, "Hey "+bh.Name+"!")
    } else {
        fmt.Fprint(w, "Hey World!")
    }
}

func runServer() {
    mux := http.NewServeMux()
    bh := &BaseHandler{Name: name}
    mux.Handle("/hey", bh)

    addr := fmt.Sprintf(":%d", 8080)
    err := http.ListenAndServe(addr, mux)
    if err != nil {
        log.Fatal(err)
    }
}

var ch = make(chan bool)

var name string
var duration int

func init() {
    flag.StringVar(&name, "name", "", "your name")
    flag.IntVar(&duration, "duration", 60, "working time")
    flag.Parse()
}

func main() {
    go runServer()

    t := time.Tick(time.Duration(duration) * time.Second)
    et := time.Tick(time.Duration(1) * time.Second)
    go func() {
        for {
            select {
            case <-t:
                ch <- true
            case <-et:
                // debug code
                fmt.Printf("[%d] server running!\n", time.Now().Unix())
            }
        }
    }()
    _ = <-ch
    fmt.Println("Finished.")
}

// https://gobyexample.com/select

Link

uokada.hatenablog.jp

influxdbをCLIから利用する

influxdb ver0.9を触っているけど、いろいろ変わりすぎているので公式ドキュメントしか頼っている。。。

ver0.6ぐらい触った時はこんな挙動だった?って思うことが沢山w

とりあえず、curlからデータを挿入してみる。

Write Syntax | InfluxDB

Specify Non-nanosecond Timestamps

Use the precision=[n,u,ms,s,m,h] query string parameter to supply a precision for the timestamps.

All timestamps are assumed to be Unix nanoseconds unless otherwise specified. If you provide timestamps in any unit other than nanoseconds, you must supply the appropriate precision in the URL query string. Use n, u, ms, s, m, and h for nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.

precisionパラメータでデータの精度を決めることが出来る。今回はテストなのでそこまで精度が必要ないので秒単位でデータを登録していく。

% curl -i -X POST 'http://localhost:8086/write?db=mydb&precision=s' \
  -d 'cput,host=server01,region=us-west value=0.64 1434055566'
HTTP/1.1 204 No Content
Request-Id: 5fa0e3de-8566-11e5-802b-000000000000
X-Influxdb-Version: 0.9.4.1
Date: Sat, 07 Nov 2015 15:44:04 GMT

バッチでデータを挿入することも出来るので大量データを挿入する場合はこっちでデータを挿入するのがベター。 ファイルのフォーマットは単発でデータを挿入する場合と一緒。

Write a Batch of Points with curl

You can also pass a file using the @ flag. The file can contain a batch of points, one per line. Points > must be separated by newline characters \n. Batches should be 5000 points or fewer for best performance.

これでもダメならUDPでデータ挿入なのかな。 Go製なのでパフォーマンスはかなり出るからこれを使うのはかなり先になりそう。

2015-10-13の艦これ戦果

5-2突破。編成はこんな感じでいった。

f:id:uokada:20151014073100j:plain

旗艦に軽空母正規空母x2、低速戦艦、重巡洋艦高速戦艦の順番。 軽空母を大破しにくいように旗艦に設定して、同じく戦艦を最後尾に戦艦を配置している。

制空権はまぁまぁ重視しているけど、BOSS戦は制空優勢ぐらいしか取れない編成で昼戦のみで十分S勝利出来た。

f:id:uokada:20151014073622j:plain

あと、ゲージ破壊時に夕雲さんGET。

f:id:uokada:20151014083932j:plain

初めての烈風GET && 摩耶改二ほか

f:id:uokada:20150922103725j:plain

【艦これ】データ厨が選ぶ!おすすめ開発レシピ | あ艦これ日和 - 艦これ攻略情報,プレイ日記

20/20/10/90のレシピで初めての烈風をGET!!
3月から始めたので半年で1つだと。。。。

f:id:uokada:20150922103935j:plain 摩耶がLv75まで成長したので改二に重巡洋艦で2隻目の改二GET。
うちには秋月型いないので防空お任せ出来る艦がやっと作れた〜


「水雷戦隊」バシー島沖緊急展開 編成と攻略 | 艦これ攻略日誌 ~艦ろぐ~

水雷戦隊」バシー島沖緊急展開も今週クリアしたけど、これ20回ぐらい2-2に行ってやっとBOSS戦 S勝利出来た!!
水雷戦隊編成だと補給船狩りには最適だけどBOSSに行かなすぎて辛い...

幕張メッセに東京ゲームショウ 2015で行ってきた!!

3年振りぐらいに東京ゲームショウに行ってきたので会場の雰囲気が伝わる写真をいくつかピックアップ。

instagram.com

instagram.com instagram.com

instagram.com instagram.com instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com

instagram.com instagram.com instagram.com

instagram.com instagram.com instagram.com

instagram.com instagram.com instagram.com

ゲームやるよりか写真取らせてもらうほうが中心だったけど行ってよかったわ〜
ちなみに、一番上の子が一番かわいいと思った。 あとその近くのブースの子が対応良かったので好印象。
2番めの4人組グループで右から2人目がその愛想が良かった子。

もうちょっとキレイに写真撮りたいわ〜 というか、フラッシュ持ってくの忘れていい写真撮れんかったorz
逆光ってどう撮ればばいいの?

あとで写真まとめなどこかのエントリ拾ってこよう。
拾ってきた。