zmq4でFizzBuzzする


初めに

Goのすごい人がついったーでgophernotes の pure go zeromq 対応が入った。とツイートしてた。
すごい人が言ってるんだから使っておいて損無いハズ。

5分ぐらい考えた結果、FizzBuzzすることに決めた。
プログラマならFizzBuzzとか数列とか使いたくなるんだからしょうがない。

できたもの

斬新なFizzBuzz結果

zeromqとは

ブローカーなしのメッセージング。ブローカー不要が何より嬉しい。
どうせ通信相手そんなにいないのに、いちいちサーバ立てたくない。
詳しくはZeroMQ参照。

FizzBuzzとは

よくやる奴。割愛。

【非検証】gophernotesを使おうとしたけど使えなかったので手順だけまとめる

gophernotesとは、Jupyter NotebookのGo言語版的な奴。詳しくはgophernotesを参照。

諸般の事情(Windowsしか持ってない)があるので、Dockerを利用する必要がある。
が、今現在Dockerが利用できる端末がない。
なので環境構築の順番だけでもまとめる。

簡単に利用するためにgopherdata/gophernotesをベースとして自分の使いやすいイメージを作る。

Dockerfile
FROM gopherdata/gophernotes:latest

RUN go get github.com/go-zeromq/zmq4 \
  && mkdir -p /etc/jupyter

COPY ./entrypoint.sh /entrypoint.sh
COPY ./jupyter_notebook_config.py /etc/jupyter/jupyter_notebook_config.py

ENTRYPOINT [ "/entrypoint.sh" ]

entrypoint.sh
jupyter notebook --no-browser --allow-root --ip=0.0.0.0  --config=/etc/jupyter/jupyter_notebook_config.py

configはgopherdata/gophernotesのイメージをいったん走らせてから、コンテナに吐いてもらう。

# コンテナ走らせて中に入る
docker run -it -d gopherdata/gophernotes
docker exec -it ${gophernotesのid} /bin/sh
# config吐く
jupyter noteboopk --generate-config
mv /path/to/jupyter_notebook_config.py /tmp/
exit
# ローカルに落とす
docker cp ${gophernotesのid}:/tmp/jupyter_notebook_config.py ./

手元で試すだけなら、jupyter_notebook_config.pyのtokenを書き変える。
後はコンテナをビルドして実行するだけ

docker build -t miyatamagophernotes:1.0.0 .
docker run -it -p 8888:8888 miyatamagophernotes:1.0.0

zmq4

まずはpub側から

publish側

1秒おきに100までのFizzBuzzを垂れ流す。

package main

import (
    "fmt"
    "time"
    "context"
    "sync"
    "github.com/go-zeromq/zmq4"
)

func main() {
    err := startPublisher()
    if err != nil {
        fmt.Printf("%v", err)
    }
}

func startPublisher() error {
    pub := zmq4.NewPub(context.Background())
    defer pub.Close()

    err := pub.Listen("tcp://*:5563")
    if err != nil {
        return err
    }
    for {
        publishFizzBuzz(pub)
        if err != nil {
            return err
        }
        time.Sleep(time.Second)
    }
    return nil
}

func publishFizzBuzz(publisher zmq4.Socket) {
    getFizzBuzzText := func(num int) string {
        if (num % 15) == 0  {
            return "FizzBuzz"
        }
        if (num % 3) == 0 {
            return "Fizz"
        }
        if (num % 5) == 0 {
            return "Buzz"
        }
        return fmt.Sprintf("%d", num)
    }

    wg := &sync.WaitGroup{}
    for i := 0; i < 100 ; i++ {
        wg.Add(1)
        go func(number int) {
            defer wg.Done()
            msg := zmq4.NewMsgFrom(
                []byte("FizzBuzz"),
                []byte(getFizzBuzzText(number)),
            )
            publisher.Send(msg)
        }(i + 1)
    }
    wg.Wait()
}

続いてsub

Subscribe側

受け取って表示するだけ

package main

import (
    "context"
    "fmt"
    "github.com/go-zeromq/zmq4"
)

func main() {

    err := startSubscribe()
    if err != nil {
        fmt.Printf("%v", err)
    }
}

func startSubscribe() error {
    sub := zmq4.NewSub(context.Background())
    defer sub.Close()

    err := sub.Dial("tcp://localhost:5563")
    if err != nil {
        return err
    }

    err = sub.SetOption(zmq4.OptionSubscribe, "FizzBuzz")
    if err != nil {
        return err
    }

    for {
        msg, err := sub.Recv()
        if err != nil {
            return err
        }
        fmt.Printf("%s\n", msg.Frames[1])
    }
}

ふりかえり

今回も様々な知見を得られた

  • FizzBuzzをgophernotes上のZeroMQでやる必要は全くない
  • gophernotesが利用できなかったので単純にzmq4でFizzBuzzしただけになった
  • 並列処理したおかげでFizzBuzzの結果が正しいかよく分からなくなった