go advent calendarの記事をいいね数でソートしてみた

こんにちわ、@smith_30です。
go4も最後となってしまったので最後にしかできない事をやってみました。

私のgoとの付き合いは今年からですがとても好きです。
書きやすい&読みやすいですし
特にgoroutineに慣れてくると色々なことを並行処理で
効率よく実行できると楽しいです。

そんなこともあってかgoを好きなエンジニアはとても多いらしく
goのアドベントカレンダーは4つもあります。

どの記事も素晴らしいとは思うのですが、いいねを多く集めてる注目されてる記事を
パパっと見たくはありませんか？

そこで、goroutineの練習がてらgoのアドベントカレンダーに投稿されている
記事をいいね順にソートして出力するスクリプトを書きました

こちら
[qiita-adv-calendar](https://github.com/smith-30/qiita-adv-calendar)

## 実行結果

```bash
$ ./build/bin/qiita-adv -n go -c 4 # goカレンダーの4つを対象にする
```

![qiita_adv.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/47896/9464f93c-6a45-cf03-5f07-4986386f2945.gif)




## 全体構成

```
                     +-------------+
        +------------+ 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)を引いておく。  

```go
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情報を取得させてその情報を受取りたいとき

```go:grid_aggregater.go
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()__実行部分

```go:calendar.go
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](https://mholt.github.io/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