Go ランタイムアーキテクチャ

シングルバイナリ、goroutine、net/http

アーキテクチャ概要

Goの特徴: 外部のアプリケーションサーバーが不要。 シングルバイナリがHTTPサーバーを内蔵し、直接リクエストを処理する。

クライアント
→ HTTP/HTTPS →
LB / Nginx
(オプション)
Go Binary
net/http + goroutines
← HTTPサーバー内蔵、外部依存なし

他言語との比較

PHP / Python / Java

Client →NginxFPM/Gunicorn/TomcatApp

複数のコンポーネントが必要

Go

Client →Go Binary (HTTPサーバー内蔵)

シングルバイナリで完結

goroutine (軽量スレッド)

従来のスレッド

OS Thread
~1MB スタック
OS Thread
~1MB スタック
OS Thread
~1MB スタック

数千スレッドが限界

goroutine

g
g
g
g
g
g
g
g
g
g
g
g
g
g
g
g
~2KB スタック (動的)

数百万goroutine可能

M:N スケジューリング: M個のgoroutineをN個のOSスレッドで実行。 Goランタイムが自動的にスケジューリング。

net/http サーバー

1package main
2
3import (
4 "net/http"
5 "time"
6)
7
8func main() {
9 // ハンドラ設定
10 mux := http.NewServeMux()
11 mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
12 w.Write([]byte("Hello, World!"))
13 })
14
15 // サーバー設定
16 server := &http.Server{
17 Addr: ":8080",
18 Handler: mux,
19 ReadTimeout: 10 * time.Second, // リクエスト読み取りタイムアウト
20 WriteTimeout: 10 * time.Second, // レスポンス書き込みタイムアウト
21 IdleTimeout: 120 * time.Second, // Keep-Alive接続のアイドルタイムアウト
22 MaxHeaderBytes: 1 << 20, // 最大ヘッダーサイズ (1MB)
23 }
24
25 // サーバー起動 (リクエストごとにgoroutineが自動生成)
26 server.ListenAndServe()
27}

自動並行処理: net/httpは各リクエストを自動的に別のgoroutineで処理。 明示的なスレッドプール設定は不要。

データフローと制限ポイント

1
Nginx / LB (オプション)
client_max_body_size, proxy_read_timeout
本番環境では推奨
2
Go net/http
ReadTimeout, WriteTimeout, MaxHeaderBytes
http.MaxBytesReader (ボディサイズ制限)
3
Go Runtime
GOMAXPROCS (使用CPUコア数)
GOMEMLIMIT (Go 1.19+ メモリ制限)
4
Application (Gin, Echo等)
フレームワーク固有の制限設定

設定詳細

リクエストサイズ制限

1// ボディサイズを制限 (例: 10MB)
2func uploadHandler(w http.ResponseWriter, r *http.Request) {
3 // MaxBytesReaderでサイズ制限
4 r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10MB
5
6 // パースエラーでサイズ超過を検知
7 if err := r.ParseMultipartForm(10 << 20); err != nil {
8 http.Error(w, "File too large", http.StatusRequestEntityTooLarge)
9 return
10 }
11
12 file, _, err := r.FormFile("file")
13 if err != nil {
14 http.Error(w, err.Error(), http.StatusBadRequest)
15 return
16 }
17 defer file.Close()
18
19 // ファイル処理...
20}

Go Runtime 環境変数

1# 使用するCPUコア数 (デフォルト: 全コア)
2export GOMAXPROCS=4
3
4# メモリ制限 (Go 1.19+)
5export GOMEMLIMIT=1GiB
6
7# GCターゲット比率 (デフォルト: 100)
8# 低いほど頻繁にGC、メモリ使用量削減
9export GOGC=50
10
11# デバッグ用
12export GODEBUG=gctrace=1 # GCログ出力

Nginx (リバースプロキシ)

1upstream go_app {
2 server 127.0.0.1:8080;
3 keepalive 32; # コネクションプール
4}
5
6server {
7 listen 80;
8 server_name example.com;
9
10 client_max_body_size 100M;
11
12 location / {
13 proxy_pass http://go_app;
14 proxy_http_version 1.1;
15 proxy_set_header Connection ""; # Keep-Alive有効化
16 proxy_set_header Host $host;
17 proxy_set_header X-Real-IP $remote_addr;
18
19 proxy_connect_timeout 10s;
20 proxy_read_timeout 60s;
21 proxy_send_timeout 60s;
22 }
23
24 # 静的ファイルはNginxから直接
25 location /static/ {
26 alias /var/www/static/;
27 expires 30d;
28 }
29}
本番ではNginxを推奨: SSL終端、静的ファイル配信、レート制限、キャッシュ

Graceful Shutdown

1package main
2
3import (
4 "context"
5 "log"
6 "net/http"
7 "os"
8 "os/signal"
9 "syscall"
10 "time"
11)
12
13func main() {
14 server := &http.Server{Addr: ":8080"}
15
16 // シグナル待受
17 go func() {
18 sigChan := make(chan os.Signal, 1)
19 signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
20 <-sigChan
21
22 log.Println("Shutting down gracefully...")
23 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
24 defer cancel()
25
26 if err := server.Shutdown(ctx); err != nil {
27 log.Printf("Shutdown error: %v", err)
28 }
29 }()
30
31 log.Println("Server starting on :8080")
32 if err := server.ListenAndServe(); err != http.ErrServerClosed {
33 log.Fatal(err)
34 }
35 log.Println("Server stopped")
36}

よくあるエラーと対処

too many open files

原因: ファイルディスクリプタ枯渇

対処:

  • ulimit -n を増加 (例: 65535)
  • コネクションのClose漏れを確認
  • http.Client にタイムアウト設定

context deadline exceeded

原因: Context タイムアウト

対処:

  • タイムアウト値を見直し
  • 下流サービスの応答時間確認
  • キャンセル伝播の確認

runtime: out of memory

原因: メモリ枯渇

対処:

  • GOMEMLIMIT を設定 (Go 1.19+)
  • pprof でメモリプロファイリング
  • goroutineリークの確認

監視項目

Go Runtime

  • • goroutine数
  • • ヒープ使用量
  • • GC回数・時間
  • • スレッド数 (OS)

アプリケーション

  • • リクエスト/秒
  • • レイテンシ (p50, p99)
  • • エラー率
  • • アクティブ接続数

Prometheus メトリクス例

1import (
2 "github.com/prometheus/client_golang/prometheus/promhttp"
3)
4
5func main() {
6 // /metrics エンドポイント追加
7 http.Handle("/metrics", promhttp.Handler())
8 http.ListenAndServe(":8080", nil)
9}
10
11// expvar (標準ライブラリ) も利用可能
12// import _ "expvar" // /debug/vars