[golang]Go&PostgresのDocker環境構築とHttpServerのTimeout処理の実装

こんにちは。KOUKIです。

とある企業でWebのシステム開発に従事しています。

本記事では、次のノウハウを記事にしました。

  1. Go言語とPostgreSQLとDockerを使った開発環境の構築
  2. http serverのTimeout処理の実装

Goのコードは、Udemyの「Learn the Why’s and How’s of concurrency in Go.」で学習したものを載せています※オススメです

Workspace

作業で使うディレクトリとファイルを用意してください。

開発環境構築

httpサーバー

Goでhttpサーバーを実装しましょう。

「Addr: “:8080”,」で、localhost:8080にアクセスするとslowHandlerを呼び出せるようにしています。注意点ですが、Docker上で動作させるアプリケーションだと「Addr: “localhost:8080″,」のようにlocalhostを指定してしまうとリクエストがコンテナに届かないので注意してください(理由は確認中)。

docker-compose.yml

postgresは、environmentにデータベースおよびユーザ名/パスワードを指定することで、コンテナ起動時に自動的に作成されます。かなり便利です。

golang docker

GoのDockerfileイメージを用意します。

reflexを導入しているので、MacやLinux環境だとホットリロードができるようになります。つまり、ソースコードを変更してもコンテナを止める必要がありません。

ちなみに、Windows環境だとreflexは動きませんでした。

ビルド

準備が整いました。次のコマンドで、ビルドしましょう。

動作確認

以下のコマンドで、コンテナを立ち上げましょう。

特にエラーがでず「Start Http Server…」が表示されたので、Postgresには問題なく接続できたようですね。

time コマンドでレスポンスを測ってみましょう。

約5sですね。「curl: (52) Empty reply from server」とかえってきますが、これでOKです。http-serverコンテナも以下のログを出しています。

TimeOut処理

http.Timeout

開発環境の構築が完了したので、次はhttp-serverのタイムアウトについて学んでみましょう。

先ほど、localhost:8080にリクエストを送信すると5s後にレスポンスがかえってきたことを確認しました。

httpサーバーでは、一定期間レスポンスがかえって来なかった場合、Timeoutしてあげる方が良いです。

Go言語では、httpパッケージのTimeoutHandlerを使うと簡単に実装することができます。

Timeコマンドを実行してみましょう。

「Timeout!」メッセージとともに約1秒でTimeoutしました。What a usefull!

このTimeoutはクライアントからのリクエストをTimeoutするだけなので、サーバー側では処理が継続しているようです。

これはよくありませんね。slowHandlerが実行されないようにしたいです。

Contextパッケージ

そんな時は、Contextパッケージが使えそうです。

Contextパッケージの詳細については、以下の記事を参照してください。

slowHandlerの*http.Requestは内部でコンテキストを持っています。このコンテキストはリクエストの状態を保持しており、このコンテキストをslowQuery関数に渡します。

slowQuery関数では、呼び出し元から渡されたコンテキストと共にDBにポーリングを行う処理に変更しました。こうすることで、何らかの理由によりコンテキストが閉じられた場合、データベースはクエリを停止しエラーメッセージを返せるようになります。

コンテキストは呼び出し元から呼び出し先へと情報を伝達させることができるので、http.TimeoutHandlerにてTimeoutになった時、このコンテキストは閉じられるはずです。

また、ついでにmain関数内のPingにもTimeoutを設定しています。

context.WithTimeoutにてコンテキストを作成しており、コンテキストの2番目の戻り値であるcancelを呼び出すことでコンテキストを閉じることができます。

そして、コンテキストを渡せるようにPingContextに変更して先程作成したコンテキストを渡しました。

動作を確認してみましょう。

今度は、クライアント側でTimeoutした場合、即座にサーバー側もTimeoutしました。slowHandlerは実行されていません。OKですね。

おわりに

Dockerを使えばめんどくさい開発環境も一瞬で構築できるので便利ですね^^

これからどんどん使って行きましょう。

ServerのTimeout処理も結構難しいかもしれませんね。自分もまだcancelの使い方が曖昧かもです。

もっと深くまで学習してGo言語のスペシャリストになりたいですね!

それでは、また!

Go記事まとめ

オススメ書籍