2010年12月22日
WebSocketを使用したWebアプリ「unkartop」を作った
※WebSocketが動くブラウザで見ないと何も起きません。Chromeがオススメ
なにこれ
unkarで表示された2chのスレッドをリアルタイムで集計して表示します。簡単に言うとapachetopのパクリ。
作った理由
デスクトップの片隅に端末を開いて、apachetopでサーバを監視し続けると出来る男の気分になれるので、毎日のようにダサい端末の黒地に白字が流れるのを見ていたら、そもそも端末でやる必要がない事に気がついたため作ってみました。
あと、unkarへのアクセスを可視化することで、Googleトレンドのように使える事を若干ながら期待しています。
動作イメージ
最近のブラウザは公式の着せ替え機能が充実しているため、かなりオシャレになりました。
詳細
このunkartopですが、今流行のWebSocketプロトコルを利用しています。
WebSocketを使用するに当たり、Go言語でWebSocket用サーバを構築しています。ソースコードは下に晒しておきます。
やっていることは単純で、unkar本体(php)で2chのスレッドが表示された際に、スレッドのURLやタイトルを格納した連想配列をJSONに変換してredisサーバに格納し、それを一秒ごとに以下のGo言語で書かれたWebSocketサーバが読みだし、適当にまとめてブラウザに送り出しています。
クライアント側は取得したデータを集計して、jQueryを使ってテーブルに当てはめているだけです。
鯖ソース(Go言語)
// うんかーTOPコマンドもどき package main import ( "fmt" "json" "redis" "os" "strconv" "http" "runtime" "time" "websocket" ) type Error string func (this Error) String() string { return string(this) } const ( RedisAddr string = "127.0.0.1:1984" WebSocketAddr string = ":12345" ) var listen_chan map[*websocket.Conn]chan os.Error func main() { runtime.GOMAXPROCS(4) listen_chan = make(map[*websocket.Conn]chan os.Error) go timeCallback() http.Handle("/top", websocket.Handler(webHandler)) err := http.ListenAndServe(WebSocketAddr, nil) if err != nil { panic(err.String()) } } func timeCallback() { // redis接続の用意 client := new(redis.Client) client.Addr = RedisAddr // 1秒毎 tick := time.Tick(1000 * 1000 * 1000 * 1) for { <- tick data, err := getData(client) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.String()) continue } for socket, errchan := range listen_chan { go writeData(data, socket, errchan) } } } func getData(client *redis.Client) ([]byte, os.Error) { bval, err := client.Get("unkocount") if err != nil { return nil, err } client.Set("unkocount", []byte("0")) size, _ := strconv.Atoi(string(bval)) if size > 10000 { size = 10000 } else if size < 1 { return nil, Error("hogehoge") } dbvals, rrerr := client.Lrange("unko", 0, size) if rrerr != nil { return nil, rrerr } // 〜省略〜 data, jeerr := json.Marshal(dbvals) if jeerr != nil { return nil, jeerr } return data, nil } func writeData(data []byte, con *websocket.Conn, errchan chan os.Error) { _, werr := con.Write(data) if werr != nil { errchan <- werr } } func webHandler(ws *websocket.Conn) { errchan := make(chan os.Error) listen_chan[ws] = errchan err := <- errchan if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err.String()) } close(errchan) listen_chan[ws] = nil, false }
※データのまとめ処理部分は省略しています。その都合でそのままだとコンパイルできないかも。
※redisのライブラリを使わせてもらっています。https://github.com/hoisie/redis.go
感想みたいなもの
Go言語でWebSocketを使用した日本語のサンプルが少ないように感じました。妙にnode.jsのサンプルっぽいものが多かったような気がします。よく見ていませんが。
今更ながら、jQueryって便利ですね。今回初めて使用したのですが、cssセレクタとやらが簡単に使用できすぎて他のライブラリが使えなくなりそうです。

