Golang製のメンテナンスしづらかった個人プロジェクトをゼロから書き直すまでの流れ

f:id:razokulover:20180416123656p:plain

以前にも推しの声優のラジオを録音するためにGolangでサーバーサイドを書いたというエントリを書いた。

razokulover.hateblo.jp

この時は爆速で環境を整えるために数日でとりあえず動くものをこしらえた。

すぐに動き出したのはよかったものの、代わりにメンテナンス性を無視してとても雑なコードになってしまっていた。

特にサーバーサイドはGolangではじめてアプリを書いたこともあり、全てmainパッケージを使っていたり、トークン等の秘匿情報もベタ書きでとても人様に見せられるものではなかった。

今回はこのサーバーサイドの雑なコードをある程度今後も手を入れやすいように書き直したのでその流れを一旦ブログにまとめてみたい。

また、まだ途中ながら一応本番で稼働する状態にはなったのでソースコードGitHubに公開してある。気になる人はみて。

github.com

以前の環境

以前の録音環境の構成をざっくり書くと、

  • 録音コマンド
  • APIサーバー

の2つがある。

録音コマンドのほうには録音したいラジオの放送時間番組情報が格納されているテーブルのIDを指定。

このコマンドを録音したいラジオ番組数分用意し、crontabに登録する(例えば、録音したい番組が10個あれば10行の録音コマンドが書かれたcron設定が行われる)。

すると、

  • 設定した時刻に各録音コマンドが実行され、
  • 番組が録音され、
  • ローカルに番組ファイルが保存され、
  • ファイルのパスと番組情報のテーブルIDをセットにしてテーブルに保存される

APIサーバーではそれらの録音した情報をテーブルから取得して、返すだけ。

これらのアプリが東京リージョンのEC2上で動く。

と、いった感じ。

問題点

とりあえず動きはじめて早半年、一部のラジオは4月の改変で打ち切りになったり、はたまた新たに録音対象に追加したい番組が増えたり等々、番組の組み換えが発生していた。

これらの追加や削除の設定は都度手動でcrontabを編集して行なっていた のだが、当たり前ながらこの作業はだるい。

このだるさが新たな視聴対象の追加を億劫にしたりする。良くない。

加えて、手で編集すると設定時間を間違えたり人為的なミスが発生したりする。

一度小倉唯yui roomを録音しようとして、局の設定を間違えて櫻井孝宏のラジオが録音されていたときはひっくり返りそうになった。

解決案

上記の問題に対処するために下記のことができるようになるのを目標にした。

  • cronの自動設定
  • 新しい番組の追加/削除の簡単化

これができれば一旦はラジオ録音の属人性が減って気軽にラジオ視聴ができるようになるはず。

実装

元の実装は汚すぎるのでゼロから書き直すことにした。

プロジェクト構成をまずはドンっ。

[radiorec] tree .
.
├── Gopkg.lock
├── Gopkg.toml
├── README.md
├── build
│   ├── radiorec-cli
│   └── server
├── cmd
│   ├── cli
│   │   └── cli.go
│   └── server
│       └── server.go
├── config
│   ├── config.example.yml
│   ├── config.go
├── internal
│   ├── cron
│   │   └── cron.go
│   ├── db
│   │   └── db.go
│   ├── filemanager
│   │   └── filemanager.go
│   ├── handler
│   │   └── handler.go
│   ├── recorder
│   │   ├── ag
│   │   │   └── ag.go
│   │   ├── radiko
│   │   │   └── radiko.go
│   │   └── recorder.go
│   └── uploader
│       ├── s3
│       │   └── s3.go
│       └── uploader.go
├── migrate
│   └── schema.sql
├── public
└── vendor

treeした結果はこんな感じ(vendorとかは省いてます)。

Gopkg.toml

パッケージ管理はdepを使った。現状のデファクトっぽいので。

/cmd

エントリポイントのファイルを設置する場所。

自分の場合は録音やcron設定用のcliAPI用のサーバーの2つがある。

/internal

色々とロジックのコードが置かれている場所。

/cmd配下のコードはここからコードをimportして使ったりする。

/internal/handler

ここにはAPIのhandlerの処理が書かれている。

薄めのフレームワークとして使ってみたかったechoを使ってみた。

net/httpでも事足りるけどただ使ってみたかっただけ。

/internal/recorder

録音する処理が詰まってる。このリポジトリの一番の肝になるコード。

/internal/recorder/recorder.goにinterfaceを定義し共通処理を行うRecord関数を定義。

各放送局特有の処理(認証とか)は/internal/recorder/radiko/radiko.go/internal/recorder/ag/ag.goへそれぞれ記述し、Recorder interfaceを満たすようにすることで同じコードのコピペ利用を避けることができた。

加えて、今後新たな放送局の追加時も局特有の処理をRecoder interfaceを満たすように追加すればいいだけなので楽チン。

/internal/cron

ここにcrontabの書き換え処理が書かれている。

新しく番組情報のテーブルに放送継続情報のカラム等を追加したので、cronコマンド生成時に放送継続情報から録音の可否を判定できるようになった。これでテーブルをいじるだけで打ち切りの番組やもう録音したくなくなった番組の録音を対象外にしてcron設定できる。

ただし、ちょっとこのコードに関しては思い切った作りになっているので注意。

録音コマンドのcron行が書かれたfileをつくり、それをcrontabへリダイレクトしてまるっと書き換える仕様になってるので既存のcron設定があったりすると死ぬ。

自分はcron設定をラジオ録音にしか使ってないけど他のコードもある人はやばいのでその辺考慮したコードを書いてもよかったかもしれない。

/config

設定ファイルにはDBの情報などが書かれている。

前から使ってみたかったviperを利用してみた。

config.ymlを用意してそこから読み込むようにしている。

CONFIG_DIRが指定されているとその場所にあるconfig.ymlを利用するようになっている。

環境ごとにconfig.ymlを設置すればコードに秘匿情報をベタ書きせずに済む。

その他

filemanagerとかuploaderとかもあるけどその辺は特に工夫はないので省略。

あ、dbではgormを使ってる。そんな深い意味はないけどこれも単に使ってみたかっただけ。

参考

Golangでのプロジェクト構成についての記事はたくさんあるが、自分はこのリポジトリを参考にした。

READMEに各ディレクトリの使用用途なども書いてあったので初心者にはありがたかった。

あと、この記事も参考になった。みんなアーキテクチャとか悩みますな...。

実装後

実装後は、

  • テーブルに番組情報登録
  • cli cronコマンドを叩く

だけで録音対象のアップデートが終わる。

手でcrontabの編集をしなくてよくなった。

また、recorderの抽象化をうまくやったので、新規の放送局追加も簡単にできるようになっている。

TODO

テスト

個人用途としてもさすがにコード量も多いし追加したい。

デプロイ

サーバーにミドルウェアの環境は整ってるので今はローカルでbuildしてscpからのcronコマンド叩いて反映みたいなシェルスクリプトを使ってる。 それだけで今は事足りてるから十分なんだけど、テスト書いたらCIしてビルドみたいな流れにしたい。

docker

ゆくゆくはコンテナで稼働させたい。 ミドルウェアのアップデートとかも楽になるし、デプロイも今っぽくなる気がする。 ここはあんまり知見ないのでバッチ環境とかAPI環境を同居させたりするのはどうなのかとか詳しい人に教えて欲しい。

他の放送局追加

niconicoとか音泉とか色々と他にも声優ラジオが聞ける局はあるのでそれらの録音にも対応したい。

まとめ

Golang製の個人プロジェクトであるラジオ録音環境を綺麗に書き直した。

1から書き直したものの、既存の問題点のみに的を絞ることで比較的簡単に実装が済んだ。

まだ今風ではない部分もあるので今後も暇な時にTODOに取り組んでいきたい。

最後に、全然関係ないけど、僕が今現在視聴しているラジオ番組を書いておきますね。

内田雄馬 君の話を焼かせて、大橋彩香のAny Beat!も聞いてたんですが、あんまりグッとこなくてついに4月になって録音対象から外しました。あと花澤香菜内山夕実のクロ香菜さんとシロ夕実さんは4月の改変で消え去りました。ゆみぽんのコーナー面白かったのに勿体無い。