みなさん初めまして。
mofur というサービスの裏方でフルスタックエンジニアとして暗躍している、新規事業部の高田と申します。
ところで、mofur ってご存知ですか?
mofur とは
過去にもこのデベロッパーズブログにて幾度も投稿されている、犬猫動画の共有サービスです。
詳しくは、Web かアプリでお確かめ下さい。
What’s golang ?
実は、mofur の裏方( Web API )は全て Go 言語(以降、 golang )で書かれています。
golang とは、 Google にて開発された非常に軽量な言語です。
C の良さに、従来の C の課題点をモダンに解決した言語だと思っていただければ、雰囲気だけでも掴めるのではないかと思います。
C の良さといえば諸説ありますが、
- 軽い
- 速い
- 型がある
- コンパイラ言語なので、コンパイルチェックが出来る(バグがなくなるとは言ってない)
- 構造体が使える
- ポインタで殴り合える(!?)
等がありますが、その一方つらい事も幾つかあります
- メモリの扱いで苦しむ事がある
- DB の制御が面倒くさい
- ネットワーク周りの制御を一から始めるとつらい
- 並列処理を書くときは真面目に考えないとつらい
- コードフォーマット戦争が始まる
- 関数ポインタで迷子になる開発者が出てくる
- 外部ライブラリ依存を考え始めると、実行環境で悩む場面が出てくる
これらの問題は全て、 golang が解決してくれています。そう、この時点でなかなか興味深い言語であるということですね。
Why golang?
さわりだけ golang について述べましたが、何故 mofur で golang が選ばれたのかについても軽く触れてみたいと思います。
みなさん、「サービスにとって重要なこと」ってなんですか?
サービスそのものが魅力的であることはもちろん、安定性や信頼性も重要です。私は仕組みとしてもっと重要視していることがあります。
それは、「十分に速いこと」。
「速さは正義」である。
これはあらゆることに対して第三者に何かを提供する場合に考えなければならないキーワードです。 golang では、その速度を非常に容易に得ることが出来ます。mofur の設計前の言語選定を行っていた当時は、他に C++/Boost という組み合わせも考えていました。それ以外に JVM 言語として Scala も検討していました。
.NET Core がリリースされた今となっては、 C# も検討対象の言語に上がってくるかも知れませんね。
他の言語でも幾らか動作の速さは求められますが、それは「狂気の域」に達した速度が出せる言語でしょうか? 更に、そのプログラムの実行環境は軽くて済むでしょうか? 考え得る選択肢の中で最高のものを選ばないと、後々妥協した問題と向かい合う時が来ます。その時、その問題は一人では太刀打ちできないくらいに大きな問題になっていることでしょう。故に、最初から狂気に触れなければならないのです。
次に私が重要視しているのは「コスト」です。当然、いろんなコストが存在します。
- 時間/開発時間
- 金銭/設備投資・設備運用
- 人/開発の困難さ
私は先ず、「人」を意識します。自分で開発を行うことになるので当然です。
私は以下のパッケージと出会うことが出来たので、開発の困難さに対する課題を乗り越えました。
- echo
https://github.com/labstack/echo - AWS SDK for Go
https://github.com/aws/aws-sdk-go - Go-MySQL-Driver
https://github.com/go-sql-driver/mysql - genmai
https://github.com/naoina/genmai
これら以外にも、 golang は元々標準パッケージが非常にリッチです。
標準でネットワーク周りの制御が容易に出来るパッケージが含まれていたり、構造体を一発で JSON に変換出来るパッケージが含まれていたりします。値を扱う場面では数学パッケージに頼ることもあると思います。暗号周りのパッケージも標準でついています。二次元画像 (jpeg, png) を扱うパッケージも標準です。 OS からのシグナルハンドリングも出来ます。
これらが標準で付いているのですから、 golang の開発環境を構築すれば、簡単なプログラムであればすぐに組むことが出来ます。当然、言語としてビット演算や高階関数、ガベージコレクションを実装しています。古典的な記法とモダンな記法を組み合わせることが出来ます。
並列処理は CSP のプロセス計算モデルを元にしたチャンネルによるスレッド間通信で実装されており、使えば使うほどに良さを感じる仕様になっています。
開発の困難さに対する問題の解決は、「時間」に対する問題も解決していきます。他の言語を書いたことがある人であれば半日程度、 C/C++ 出身者であればものの数十分で書き始めることが可能です。
事実、 mofur 開発時は比較的スムーズに開発を進めることが出来ました。言語そのものがシンプルで、便利だけど系が複雑化していく機能が実装されていないため、書き進めても困難な問題に遭遇することがほぼありませんでした。ですが、シンプル故に他言語よりも関係者の設計力と先を見通す力を問われます。「他の言語ではこうやってたし〜」とか、「とりあえずこんなもんでいいやろー」という投げやりな設計は、必ず後から大きな問題を生んでしまいます。
「金銭」については、 golang の言語特性と echo の性格によりエコシステムを構築できることが分かり、解決しました。軽い言語は安いサーバでも十分動くということです。この点は、私のスキルではどう頑張っても JVM 言語では達成が出来ませんでした。
Nice specification of golang.
golang には色々な素晴らしい仕様があります。
先ずは、猛烈にシンプルな言語であることです。(異論は認める)
他の言語を学んだ人からすると、「え、これだけ?」と思うこともありますし、「え、○○では既にあった機能なのに、 golang にはないの!?」と思うこともあります。
しかし、これは良いことです。仕様が簡素なため、簡素なコードを書くことを強いられます。普通に書けば処理を追いやすいコードになるので、後々の保守で困ることは多くありません。
例えば、 Java 出身者であればジェネリクスが存在しないことに対して理解に苦しむ場面もあるでしょう。しかし、そこは「 Go に入れば、 Go に従え」の精神で golang の考え方に慣れることが重要です。 golang には interface 型というものが存在します。 golang を書く上でジェネリクスを求める場面では、実は他にやりようがあると言うことに、ある程度書き続けることで気づくことになります。
次に、並列処理が非常に簡単にできることです。
goroutine と channel という仕組みを使うことで、実行時は非同期で、最後に処理を同期させる、といったことも可能です。 mutex lock も存在するので、排他ロックで擬似的な非同期処理もカジュアルに書くことが可能です。シングルコアであったとしても、背後に DB へのアクセスが生じるような処理を非同期に閉じ込めれば、それだけでも最適化出来る箇所は出てきます。
また、投げっぱなしの非同期処理も実現可能です。非同期実行した処理の結果がどうなったとしても構わないが、同期させると重いような処理を行う場合に最適です。
配列の扱いも悪くありません。 golang では、配列型に於いて値が積まれたかどうかを簡単に判断できます。
var as []string // 文字列型配列 as を定義 as = []string{ "フェンリル", "デベロッパーズ", "ブログ", } // 配列に値を積み込み // golang では、if文内で有効な変数を定義してから評価することが可能 if ret := len(as); ret > 0 { println("配列が0件以上です") } else { println("配列は空です") }
上記のような定義をすれば、下記のように処理できます
// foreach 的な for 文で書いてみる例 for i, s := range as { println(i + 1, "個目には\"", s , "\"が格納されています") }
実に簡単ですね。更に、この文字列( string 型)を「文字の配列」と見做して文字単位で処理を行うことも可能です。この辺りは、 C/C++ の char 型配列の発想に近いですね。
// foreach 的な for 文で書いてみる例 for i, s := range as { for j, r := range s { println(i, ",", j, ":", string(r)) } }
[]string を解体して string にした後、それを配列と見做すと rune という単位で取得可能です。
なんとこの rune 、マルチバイト文字単位でとり出されます。 Web や iOS/Android から流れ込んでくる文字列には、最近世界的にも流行の Emoji (絵文字)も混ざっていますよね。
そんな時、この仕様はかなりありがたいと思いませんか? (サロゲートペアでも恐れる必要はありません)
当然、連想配列( map )も存在します。
// 定義 fenrir_products := map[string]string{ "Sleipnir":"スレイプニル", "mofur":"モフール", "Picky-Pics":"ピッキーピクス", } // 取り出し println(fenrir_products["Sleipnir"]) // "スレイプニル" println(fenrir_products["mofur"]) // "モフール" println(fenrir_products["Picky-Pics"]) // "ピッキーピクス" // 存在確認 if v, ok := fenrir_products["mofur"]; ok { // ok is "true" println(v) // "モフール" } else { println("map内に定義なし") } if v, ok := fenrir_products["Brushup"]; ok { // ok is "false" println(v) } else { println("map内に定義なし") // "map内に定義なし" }
うーん、便利ですね。
他にも伝えたい golang の魅力は色々ありますが、今回で全てを扱い切ることは出来ないので、この辺りで自重しておきますね。
Development environment.
私は開発環境に JetBrains 社の IntelliJ IDEA を使っています。ここに、 golang plugin を導入することで、開発環境としています。色々触ってみましたが、私はこの組み合わせが最もしっくり来ました。
IntelliJ IDEA 以外には、 GitHub の Atom 、 Microsoft の Visual Studio Code などでも golang の開発環境が整いつつあります。当然、 Vim や Emacs でも golang plugin が存在しています。
IDE やエディタについて追求していくと宗教戦争に発展してしまうので、この辺りで。
- IntelliJ IDEA
https://www.jetbrains.com/idea/ - Atom
https://atom.io/ - Visual Studio Code
https://code.visualstudio.com/
Finally.
golang を mofur で採用した理由について、軽く触れてみました。
どうでしょうか。 API サーバを書きたいけど、今まで取り敢えずで開発言語を選定してきたんだよねって方、是非「 golang を使ってみよう!」と提案してみて下さい。悪くないはずですよ。
次回は、 mofur で golang とどうやって付き合ってきたかについて説明してみたいと思います。
後編はこちらから
mofur のオフィシャル Twitter アカウントでは、 mofur 内のおすすめの動画や、 mofur 内のキャンペーン情報をつぶやいています。
話しかければ、中の人からの返事も貰えるかも……!
よろしければフォローしてください!