golangで商業サービスを書いて世の中に送り出した話
やあやあ。
今回は Go Advent Calendar 2015 その2 の12日目の投稿だよ。
今年のgolangは人気で、その1~その3まであるみたい。
11日目の投稿はこちらの方達がされています
- curepine : Golangで画像をglitchできるライブラリの紹介
- hama_du : Goで型を挿げ替え可能なデータ構造ライブラリを作る
- yuin@github : Goのインタフェースがパフォーマンスに及ぼす影響
全体的に技術的にレベルが高い記事が多い中、私は違う視点からAdvent Calendarを書いてみたいと思います。
なんで商業サービスにgolangを採用したの?
アーキの設計権限も、言語の採用権限も、全て私にありました。
私がサービスで最も重要なのは、挙動の「速さ」であると考えています。
速いは正義。速さこそ全て。速さとは力である。
それと同時に、ある程度の書きやすさも必要です。作るのは私です。書きづらい言語は避けたい所です。
後は、予算は節約する必要があるので、クラウドで借りれる安いヘボサーバを並列で並べて何とかしたいという大人の事情。ほどほど~良いサーバを数台借りるより、遥かに安上がりなんですよね。
今回私が書くのはサーバサイドのアプリケーション。APIサーバと分類されるものです。
そこでいくつかの言語が候補に挙がりました。
え? C++以外名前が伏せられているじゃないかって? まあ、その辺は想像にお任せします。
関数的表現が可能なJVM言語
初心者にしては比較的動くサンプルコードが書けたと思いました。
ただ、どうしても以下の問題を私には解決できませんでした。
- 安定稼働させるには思ったよりもメモリを必要とした
- 思ったほど速度が出なかった
安定はしていたのですが、如何せんリソース食う割に速度が出ずに頭を抱えました。きっともっとうまく書ける人も居るんでしょうが、私にはこの程度しか使いこなせませんでした。
後、関数的に書くと私以外の人の学習コストが高すぎるので、エンジニアの並列化がやりづらいという別の問題も。
コンパイル不要なメージャーどころのスクリプト系言語
最近最新版が出て速度がかなり改善された言語ですが、私はコンパイル駆動がしたかったので、お引き取り頂きました。
そのスクリプト言語と同じような記法で書けてコンパイルできる言語
上記言語が高速化する前に一時期話題になった噂の言語。
ですが、今後もその言語に縛られる事は幸せなのかどうかで悩んで、一旦保留にしました。
C++
私はC/C++系の出身なので速度的にはかなり有利な事が分かっていましたが、それと同時にこの言語でネットワーク周りをやると死んでしまいそうになる事も知っていたので、却下しました。
そして選ばれたのはgolangでした
golangは満足な速度が出ます。そして、コンパイラがそれなりに優秀です。
ネットワーク周りや暗号処理周りが標準パッケージに含まれており、それらの出来も結構良いです。ここで、ネットワークが必要となるプログラミングにも十分耐えられることが分かりますね。
better Cでもあるので、構造体が使用可能です。「ある」と「ない」とでは全く違います。更に、ポインタ概念も存在します。にも拘らず、メモリの開放は言語にGCが積まれているので、C/C++に存在しがちなメモリの開放に関わる諸問題が起きづらいです。
なかなか魅力的ですね。
また、言語の機能そのものも非常にシンプルで、ちょっと勉強したら書き始める事が出来ます。
golangでプロダクトを書くにあたり
最初に行ったのは以下の整備です
これだけはしないといけません。しかし、これだけやれば後は書くだけです。
ビルド~デプロイ系
ビルドスクリプトを叩けば、勝手にプロダクトがビルドされるようにしておきました。
デプロイはJenkins氏に丸投げします。勝手にgitからリポジトリを落としてきて、勝手にビルドして、勝手に成果物を目的の場所に配信する仕組みを作るだけです。
また、外部パッケージは毎回最新を取得してビルドするようにしました。
私は台数が変動し、IPも固定に出来ない複数台のヘボサーバへ自動に配信したかったので、動的分散デプロイシステムを書いています。
RDB周り
gplangには重厚なORMは不向きです。直接SQLを書くことの方が多いでしょう。
ルーティング系
良いパッケージを見つけたので、そちらを利用させていただきました。
外部パッケージのバージョン制御
恐らく、ここがgolangの弱点。
golangは外部パッケージをコマンド一本で取得して利用できる強みがありますが、この機能は対象リポジトリから常に最新のものを取得してくるという性格を持っています。
golangの設計思想として、外部パッケージは公開した後に破壊的アップデートをしてはならないとなっているようで、利用しているパッケージが下手なアップデートをしてくると死にます。
ただ、私は常に最新のコードを使いたかったので、クリティカルなパッケージのみ、自動取得した後に特定バージョンにチェックアウトしてコードを利用するというサブコマンドを書きました。
プロダクトごとに設定ファイルを持ち、デプロイ時にその設定ファイルに従って特定のバージョンに変更してからビルドをするというような動き方を実現しています。
golangでプロダクトを書いていて困ったこと
先ず、循環参照が出来ない点。golangにはC++のようなヘッダファイルが存在しないので、構造体を定義してパッケージ間で投げ合う時に困る事があります。この時は、ヘッダ用のパッケージを定義し、その配下に本体コードのパッケージを定義する事で回避が出来ました。
次に、型変換を容易にする標準機能が思ったより貧相であること。私は、型変換用のパッケージを別途設計して、全てそれを使いまわすようにしました。実に便利です。
最後に、真のマルチスレッドに対応していない外部パッケージがちらほら存在すること。sql driver周りで嵌りました。泣く泣く、スレッド数を1固定にもどしました。ヘボサーバは1コアだから悔しくなんてないんだからね(グスン
golangでプロダクトを書いていて気を付けたこと
先ず、各機能に「状態」を持たせないようにしました。
誰かに呼ばれるのは一期一会。どのように呼ばれようと、同じ値が同じ環境に投入されると、同じ結果が返ってくるように書いていきました。関数的表現のエッセンスを取り入れた結果です。
次に、構造体をなるべく使うようにしました。実に便利です。ポインタを投げ合うの、楽しいです。各メソッドのI/Fにも影響が出づらいので、良いです。
そして、goroutineとchannelを上手く使い、並列化できる処理は並列化しまくりました。
最後は、各処理の独立性を上げた構造を意識していきました。
これぐらいの注意点で、十分動くものが作れます。
最後に
goに入ってgoに従う
これですよ。goにはgoの書き方がある。それに従えば何とかなる。
え? これが言いたかっただけだろって? ハハハ
goと共にあらんことを。/ May the Golang be with you.
技術的なお話をあえて避けて、実際に使った人の視点で書いてみました。
golangでプロダクトを書きたい方、安心してください、怖くないですよ。
ちゃんと書けます。だから、怖がらずに採用しても大丈夫な言語です。
明日は Go Advent Calendar 2015 その2 の13日目となります。
13日目の投稿はこちらの方達です。
- kyokomi : GAE/GoでSlackの'/'(スラッシュ)コマンドサーバーを立てる
- shogo82148 : Goでデプロイ神社書いてみた
- tomoya0x00 : Goでのシリアル通信でハマった事
CM
こんなのやってます
ではでは!