2013/12/25

Recent entries from same category

  1. golang では変数の宣言位置が大事
  2. golang と Generics と私
  3. レーベンシュタイン距離を使ったあいまい grep コマンド「lsdgrep」作ってみた
  4. golang オフィシャル謹製のパッケージ依存解決ツール「dep」
  5. Re: Go でシングルバイナリな Web アプリを開発しているときに webpack --watch をうまいところやる

Golang ってネットワークを扱うのが凄く楽で色んな物が作りたくなるんだけど、いつも pubsub っぽいのが欲しくなって毎回作ってる気がしたので汎用的に扱えるインタフェースを作った。
mattn/go-pubsub - GitHub
https://github.com/mattn/go-pubsub
使い方は簡単で、まず subscribe 側はある型を引数に持つコールバックで Sub を呼び出す。
ps := pubsub.New()
ps.Sub(func(i int) {
    fmt.Println("int subscriber: ", i)
})
そして publish 側はある型を指定して Pub を呼び出す。
ps.Pub(1)
Pub された値と同じ型の引数を持つ subscriber のみがメッセージを受け取れるという仕組み。構造体も渡せるので複雑なメッセージ内容もOK。
ちなみに unsubscribe は Sub に渡したコールバック関数を Leave に渡すか、コールバック内で
ps.Sub(func(f *foo) {
    fmt.Println("foo subscriber: ", f.bar)
    ps.Leave(nil)
})
この様に nil を与えると呼出中のコールバックが unsubscribe されます。 簡単に chat を作ってみました。
package main

import (
    "bufio"
    "github.com/mattn/go-pubsub"
    "log"
    "net"
    "strings"
)

func main() {
    ps := pubsub.New()

    l, err := net.Listen("tcp"":5555")
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Listing", l.Addr().String())
    log.Println("Clients can connect to this server like follow:")
    log.Println("  $ telnet server:5555")
    for {
        c, err := l.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go func(c net.Conn) {
            buf := bufio.NewReader(c)
            ps.Sub(func(t string) {
                log.Println(t)
                c.Write([]byte(t + "\n"))
            })
            log.Println("Subscribed", c.RemoteAddr().String())
            for {
                b, _, err := buf.ReadLine()
                if err != nil {
                    log.Println("Closed", c.RemoteAddr().String())
                    break
                }
                ps.Pub(strings.TrimSpace(string(b)))
            }
            ps.Leave(nil)
        }(c)
    }
}
やばい。簡単すぎる。

blog comments powered by Disqus