Go ランタイムアーキテクチャ
シングルバイナリ、goroutine、net/http
アーキテクチャ概要
Goの特徴: 外部のアプリケーションサーバーが不要。 シングルバイナリがHTTPサーバーを内蔵し、直接リクエストを処理する。
クライアント
→ HTTP/HTTPS →
LB / Nginx
(オプション)
→
Go Binary
net/http + goroutines
← HTTPサーバー内蔵、外部依存なし
他言語との比較
PHP / Python / Java
Client →Nginx→FPM/Gunicorn/Tomcat→App
複数のコンポーネントが必要
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 サーバー
1 package main 2 3 import ( 4 "net/http" 5 "time" 6 ) 7 8 func 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 (ボディサイズ制限)
http.MaxBytesReader (ボディサイズ制限)
3
Go Runtime
GOMAXPROCS (使用CPUコア数)
GOMEMLIMIT (Go 1.19+ メモリ制限)
GOMEMLIMIT (Go 1.19+ メモリ制限)
4
Application (Gin, Echo等)
フレームワーク固有の制限設定
設定詳細
リクエストサイズ制限
1 // ボディサイズを制限 (例: 10MB) 2 func 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コア数 (デフォルト: 全コア) 2 export GOMAXPROCS=4 3 4 # メモリ制限 (Go 1.19+) 5 export GOMEMLIMIT=1GiB 6 7 # GCターゲット比率 (デフォルト: 100) 8 # 低いほど頻繁にGC、メモリ使用量削減 9 export GOGC=50 10 11 # デバッグ用 12 export GODEBUG=gctrace=1 # GCログ出力
Nginx (リバースプロキシ)
1 upstream go_app { 2 server 127.0.0.1:8080; 3 keepalive 32; # コネクションプール 4 } 5 6 server { 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
1 package main 2 3 import ( 4 "context" 5 "log" 6 "net/http" 7 "os" 8 "os/signal" 9 "syscall" 10 "time" 11 ) 12 13 func 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 メトリクス例
1 import ( 2 "github.com/prometheus/client_golang/prometheus/promhttp" 3 ) 4 5 func main() { 6 // /metrics エンドポイント追加 7 http.Handle("/metrics", promhttp.Handler()) 8 http.ListenAndServe(":8080", nil) 9 } 10 11 // expvar (標準ライブラリ) も利用可能 12 // import _ "expvar" // /debug/vars