こんにちわ、@smith_30です。
go4も最後となってしまったので最後にしかできない事をやってみました。
私のgoとの付き合いは今年からですがとても好きです。
書きやすい&読みやすいですし
特にgoroutineに慣れてくると色々なことを並行処理で
効率よく実行できると楽しいです。
そんなこともあってかgoを好きなエンジニアはとても多いらしく
goのアドベントカレンダーは4つもあります。
どの記事も素晴らしいとは思うのですが、いいねを多く集めてる注目されてる記事を
パパっと見たくはありませんか?
そこで、goroutineの練習がてらgoのアドベントカレンダーに投稿されている
記事をいいね順にソートして出力するスクリプトを書きました
実行結果
$ ./build/bin/qiita-adv -n go -c 4 # goカレンダーの4つを対象にする
全体構成
+-------------+
+------------+ aggregateCh <--------------------------------------------------------------+
| +------+------+ |
| | +-----------+ |
| | +-> +----------+ | +--+ |
+------v------+ +-----+------+ +------------+ | +---------+ +-+ | +------+------+
| []*GridData +------+ Aggregater +---+ Dispatcher +-------> +--------+ +-+ +--------> | aggregateCh |
+------+------+ +-----+------+ +-----+------+ | | Fetcher+-+ | +-------------+
| | | +-> +---^----+ +--+ +-----------+
| | +--+----+ | +-------------+ | |
+---+----+ | +----------> queue | +--------------------+ Req / Res +--------------------> qiita api |
| | | | +-------+ +-------------+ | |
| Sort | +------+--+----+ +-----------+
| | | gridUpdateCh <--------------------------------------------------------------------------+
+---+----+ +--------------+ +------+ |
| +----------+ +-> | Grid | +-----------+ |
| +------> | +----------------+ +------+ | |
Finish ^ | +-+--------+ | | |
+----+---+ +----> | +---+ +------+ +-----------------------+ | |
| | Start | +-+--------+ | | +--> Grid +-+ + | | |
| Output | +----------------+ | | | +-+ +------+ | | | |
| | | GridAggregater +------+---> | Calendar +-+ +-------+ +v-v--+--------+
+--------+ +----------------+ | +----+ +-------------------> | gridUpdateCh |
+----------+ | +------+ | +--------------+
+> | Grid +-+ +-+
+------+ |
+-------+
やってること
- AggregaterがgridUpdateCh(qiita apiに投げるためのGrid)を引いておく。
type Grid struct {
URL string
QiitaURL string
Title string
Like int
}
- GridAggregaterがカレンダー数分のgoroutineを動かす(Calendarが管理)
- Calendarが受け取ったurlにあるGridを取得する(go-queryを使用)
- Gridには外部のブログリンクもあるのでqiitaの記事のみGridとしてセットしgridUpdateChに投げる
- AggregaterはgridUpdateChからGridを取り出してdispatcherのqueueに流し、dispatcherがfetcherに渡す
(dispatcherをかませているのはgoroutineの数を制御するため) - fetcherはGridの持つURLをqiita apiに投げていいねの情報を埋め込みaggregateChに投げる
- aggregateChからGridを引いてスライスに溜め込む。
- すべてのgridの情報が更新されたらaggregateChを閉じる
- スライスに入ったGrid情報をいいね順に並び替えて出力する
実装して学んだこと, tips
- チャンネルを通してデータの連携を行う時はデータを受ける側が先にチャンネルを引くようにする
ex.)カレンダーにGrid情報を取得させてその情報を受取りたいとき
func (cs *GridAggregater) FetchGrids(gridUpdateCh chan *model.Grid) {
for _, ca := range cs.C {
cs.wg.Add(1)
go func(c *model.Calendar) {
cs.logger.Infof("calendar %s is start.", c.URL)
// 裏でgoroutineを動かす
// goroutineが書き込むチャンネルを受け取る
gridCh := c.SetExecuteURLs()
for g := range gridCh {
// channelから引いた値を別のチャンネルに送る
gridUpdateCh <- g
}
cs.wg.Done()
}(ca)
}
}
__c.SetExecuteURLs()__実行部分
func (c *Calendar) SetExecuteURLs() <-chan *Grid {
gridCh := make(chan *Grid, 25)
go func() {
defer close(gridCh)
//
// カレンダー内のgrid情報を取得する -> g
//
gridCh <- g
}()
return gridCh
}
処理が終わったらcloseすることで、channelをforで引いている側の処理が終わる。
for でチャンネルを引いている部分はチャンネルが閉じられると
送信されたデータをすべて引いた後に処理を抜ける。
処理中の gridUpdateCh
も事前にfor rangeでデータを引けるようにしています
- apiでのレスポンスは json-to-go使うと便利
-> jsonはっつければgoのstructに変換してくれる。
- dispatcher - workerの仕組みは参考にのっけたのでgo歴の浅い方は参考にされるとよいかと思います
僕も参考にしてほぼコピペで使わせてもらいました。
ほんとに簡潔に書くと
- dispatcherはworker(目的の処理を行うgoroutine)をpoolとして決まった数もっている。
- dispatcherはqueue(interface{})を持っていて、そのqueueにデータを流すと
自身が持っているworkerを取得し、workerにデータを渡すことで処理を委譲しています。
- workerは親であるdispatcherを持っていて、そのdispatcherに自身が行った処理の終了通知をして
親のdispatcherのpoolに戻るようになっている
単なる親子関係でないっていうのが面白い。。
さいごに
ちなみに今のところ一番上に来るのは mattn
さんの記事でした!
171, 男と女が寄りそうとどうなるのか, https://qiita.com/mattn/items/14229755ac9427ecdd1f
57, Goでテストを書く(テストの実装パターン集), https://qiita.com/atotto/items/f6b8c773264a3183a53c
53, Go言語で「なかった」の返し方, https://qiita.com/najeira/items/0bb0acdd7a71fc3f559b
.
.
.
25日時点なので直近での記事は上位に来づらいことはお許しください<(_ _)>
参考
画面キャプチャ(gyazoではうまいこと収まり切らなかったので)
https://qiita.com/YosukeItabashi/items/5722395218e6e592fd39
dispatcherでのgoroutine制御
http://blog.kaneshin.co/entry/2016/08/18/190435
ascii generater(テキストからasciiに変換してくれるやつ)
http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20