はじめに
こんにちは。ここ最近Rails以外の言語を触ることがなくなってきた、また今後の可能性としてプライベートでGo言語によるWeb開発を進めるかもしれないため、今回はGoデビューとのことでここにそのエビデンスを残します。Goに関しては知識がほとんどないため、まずは資料集めです。それではいってみましょう。
なぜGo言語を選ぶのか?
ある友人からGo言語は学習コストが低いからとそれまでRailsで開発していたプロジェクトをGo言語にリプレースしていると言うことを聞きました。Go言語の学習コストが低いかどうかはともかくとして、僕が興味を持ったのは事実であり、実際にGo言語を強く推しているエビデンスを探してみると以下のような記事がありました。
要約程度に
* ハードウェアの進歩は年々向上しているが、シングルスレッドのパフォーマンスとプロセッサの周波数は頭打ちになってきた
* 上の問題を解決するために、プロセッサにキャッシュの追加やハイパースレッディング、コアを増やすと言った解決法を行ってきたが物理的な限界はくるし、コストもかかる
* したがってハードウェア以外の観点からパフォーマンスを向上させるためにソフトウェアでパフォーマンスを向上させる
* 現代のプログラミング言語(Java, Pythonなど)は最初はシングルスレッドでの開発のみを想定していた
* Go言語は初めからマルチスレッドによる開発を想定して実装されている
* Goはスレッドの代わりにGoルーチンを採用している
* GoはJava,Pythonと言った高級言語とC,C++と言った低水準言語の良い所取りだ
* Go言語はクラスや継承、例外などをサポートしていない
英語は勉強中ですが、なんとなくの理解では読めました。記事を読んでいたのですが、これで学習コストが低いと言われてもあまりピンときていませんが、しかし最初からパフォーマンスを考慮して設計された言語であるというのは、とても魅力的な言語であることは確かです。これでGo言語を学ぶモチベーションは出来たので実際に開発に進んでいきましょう。
Goのインストール
まず初めに、Goを自分の環境にインストールする所から始めます。ここ最近は、グローバルに言語をインストールするのを辞めて、rbenvやpyenvと言ったようなプロジェクトリポジトリ毎にプログラム言語のVersionを管理できるツールを使ってインストールしています。
Goにもそういったものあるかなー?と思って調べたらすぐ出てきました。
https://github.com/syndbg/goenv
このgoenvを使ってGoをインストールします。
% mkdir dairy_report
% cd dairy_report
% goenv install 1.11.0
% goenv local 1.11.0
goenvでインストールしたGoを実行できるようにPATHの環境変数にgoenvのディレクトリを指定してあげます。
export PATH="$HOME/.rbenv/bin:$PATH" # rbenv
export PATH="$HOME/.pyenv/bin:$PATH" # pyenv
export PATH="$HOME/.goenv/shims:$PATH" # goenv
export RBENV_ROOT="$HOME/.rbenv"
eval "$(rbenv init -)"
eval "$(pyenv init -)"
eval "$(goenv init -)
また、Goでは開発時の作業ディレクトリとしてGOPATH
の環境変数で指定するディレクトリの指定が必要となります。
このGOPATH配下のディレクトリに必要なパッケージ群がインストールされます。今回は、プロジェクト毎にGOPATHを分けたいため、direnv
を利用して必要な環境変数をプロジェクト特有のものとして読み込めるようにします。
direnvについてはこちら→https://github.com/zimbatm/direnv
% brew install direnv
EDITOR=vim
eval "$(direnv hook zsh)"
direnvでは、ディレクトリ直下に.envrc
というスクリプトファイルを作成しこの.direnvを読み込むことでプロジェクトの特有の設定を行ってくれます。
まずは、この.envrcにGOPATHの指定のみをしてあげましょう。direnv edit .
により.envrcを作成してくれますが、この時事前にEDITOR
環境変数で指定したエディタが実行できるようにしてあげる必要があります。環境変数程度の編集のつもりなので、vimにしておきます。ここのvendorの指定は後述するgomによるパッケージ一括管理のツールでインストールするパッケージの場所と統合させるために同じ設定をしておきます。更に最後にbinをPATH指定しておきたいため、PATHの指定でGOBINの定義を追加しておきます。
export GOPATH="$(pwd)/vendor/"
export GOBIN="$(pwd)/vendor/bin"
export PATH="$GOBIN:$PATH"
以上で保存を行えばloadingしてくれます。
direnv: loading .envrc
direnv: export +GOPATH
% echo $GOPATH
~/Desktop/Develop/dairy_report/go:
Goチュートリアル
公式サイトのチュートリアルに大体の事は書かれています。これを参考にコードをゴリゴリ書いていきたいと思います。
https://tour.golang.org/welcome/1
Go言語によるWeb開発
Go言語によるWeb開発のお作法的なところを知りたかったので、以下のようなスライドも参考にしていました。
著者がRubyistだったため、僕の場合でも適合できると思い十分に参考になりました。
フレームワークについて
さて、いざGo言語によるWeb開発を進めていくわけですがその前にGoで使われている主流なフレームワークは何があるのか調べていました(前述のスライドでスタンダードのようなものはまだないとの事ですが)。
そんな中で以下のようなブログを見つけました。
Why I Don’t Use Go Web Frameworks
要約するとこんな感じ
* Go言語自体がすでにWebフレームワーク化している。
* 多くのGo言語のフレームワークはHTTPリクエストとレスポンスを隠して抽象化したメソッドを提供している。
* しかし、それはわざわざ単純にできることをパフォーマンスを低下させてまでやっているのではないか
気になったのは上の三文でしょうか。要はGo言語は元々net/http
のスタンダードなライブラリで大体のことはできるのでわざわざフレームワークを使う必要がないと読めました。ということで本当にそうなのか、検証して試していきたいと思います。
ひとまずnet/httpでWebサーバーを書いてみる
Goのプログラムは、packages
で構成されます。プログラムはmain
パッケージから開始させるため下記のプログラムで実行時に8080番ポートで待ち受けを行うサーバープログラムを書いてみました。
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/",
func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
log.Printf("%+v¥n", r)
})
http.ListenAndServe(":8080", nil)
}
以上で、http://0.0.0.0:8080 にアクセスすると以下のような結果が返ってきます。
ここからゴリゴリコードを書いていくわけですが、その前に開発しやすいようにそれぞれの環境を整えていきます。
パッケージマネージャー
長らく、Railsに親しんできたので今回はgomというパッケージマネージャーを採用しました。
先ほど、goenvとdirenvでGOPATHはプロジェクト配下のディレクトリでインストールするようにしたいので、以下のようなコマンドでgomを取得するようにしました。
% goenv exec go get github.com/mattn/gom
使い勝手はBundler, Gemfileに似ているのでこれを採用することにしました。
マイグレーション
DBのバージョン管理を行うためにマイグレーションツールとしてgoose
を採用することにしました。
これをgomで一括管理できるようにGomfile(Gemfileのようなもの)に追記することとしました。
gom bitbucket.org/liamstask/goose
追加したgooseパッケージをgom経由でインストールできるようにします。
今度はgom経由でインストールするため、前述で指定したPATHの指定によりbin配下のgomを呼び出せるようになっているため、gomを実行するだけでOKです。
% gom install
downloading bitbucket.org/liamstask/goose
# bitbucket.org/liamstask/goose/db-sample/migrations
runtime.main_main·f: function main is undeclared in the main package
gom: exit status 2
異常終了していますが、これでgooseをインストールすることができました。
% goose
goose is a database migration management system for Go projects.
Usage:
goose [options] <subcommand> [subcommand options]
Options:
-env string
which DB environment to use (default "development")
-path string
folder containing db info (default "db")
-pgschema string
which postgres-schema to migrate (default = none)
Commands:
up Migrate the DB to the most recent version available
down Roll back the version by 1
redo Re-run the latest migration
status dump the migration status for the current DB
create Create the scaffolding for a new migration
dbversion Print the current version of the database
Commit
ここからいよいよ進めていくわけですが、長くなりそうでしたのでここで一旦コミットすることとします。
.gitignoreでは以下のような設定を行いました。
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
*vendor/
*.idea
*.envrc
% git add .
% git commit -m "first commit"
[master 72e578c] first commit
4 files changed, 34 insertions(+)
create mode 100644 .gitignore
create mode 100644 .go-version
create mode 100644 Gomfile
create mode 100644 main.go
% git push origin master
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 812 bytes | 812.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:himrock922/dairy_report.git
bfb5b01..72e578c master -> master
まとめ
今回は事前準備しか行っておらず、あまり深い所までいけていませんが、次からはコードを書いていこうと思います。
今日は一旦ここまで
追記
% gom install
downloading bitbucket.org/liamstask/goose
# bitbucket.org/liamstask/goose/db-sample/migrations
runtime.main_main·f: function main is undeclared in the main package
gom: exit status 2
この問題の原因がわかりました。
go get
はリモートリポジトリからソースを取得した後にbuildを実行しています。
そのbuildの際にマイグレーションサンプルファイルのディレクトリの中にmain関数が存在するGoプログラムがないということでエラーを起こしています。
build時にこのディレクトリを除外できないものでしょうか。いずれにせよバグ挙動のようなので後々issue報告しておきます。
更に追記
すみません。ちゃんとgo getのディレクトリ先の指定パスを読んでいませんでした。
https://bitbucket.org/liamstask/goose/src/master/
Install
$ go get bitbucket.org/liamstask/goose/cmd/goose
This will install the goose binary to your $GOPATH/bin directory.
ということなので、ちゃんとcmd配下のみのバイナリをインストールするようにGomfileを書き換えます。
gom 'bitbucket.org/liamstask/goose/cmd/goose'
% gom install
downloading bitbucket.org/liamstask/goose/cmd/goose
これにより、正しくgooseをインストールすることができました。
参考文献
- https://tour.golang.org/welcome/1
- https://github.com/zimbatm/direnv
- GoによるWebアプリ開発のキホン
- Why I Don’t Use Go Web Frameworks