http.Get()
の裏側、説明できますか?私たちは毎日net/http
パッケージを使っています。しかしhttp.Get("https://example.com")
の一行が実行された瞬間、内部では一体何が起きているのでしょうか?
この一行の背後では、DNS,TCP,TLS,HTTPといった複数のネットワーク層にまたがる複雑な処理が実行されています。この記事では、Goエンジニアが知るべきネットワークの基本階層を、Goの標準ライブラリと関連付けながら探訪していきます。
net/http
の世界役割: 私たちが直接やり取りするプロトコル(HTTP,DNSなど)の層。「何を」「どのような形式で」やり取りするかを定義します。
Goとの関連: net/http
パッケージがこの層の複雑さを抽象化しています。
net
パッケージとTCP/UDP役割: アプリケーション間の、実際のデータ転送を担う層。データの「届け方」を定義します。
Goとの関連: net
パッケージが、これらのプロトコルを直接扱うためのインターフェースを提供しています。net/http
は内部でTCPを利用しています。
go1// import "net" 2 3// TCPサーバーの例 4listener, err := net.Listen("tcp", ":8080") 5if err != nil { 6 // エラーハンドリング 7} 8 9// TCPクライアントの例 10conn, err := net.Dial("tcp", "example.com:80") 11if err != nil { 12 // エラーハンドリング 13}
http.Transport
Go ネットワークプログラミングでは、デフォルトのhttp.Transport
が内部でコネクションプールを実装しています。大量のリクエストを高速に捌くサービスでは、以下のようなチューニングが効果的です。
go1tr := &http.Transport{ 2 MaxIdleConns: 100, 3 MaxIdleConnsPerHost: 10, 4 IdleConnTimeout: 90 * time.Second, 5 DialContext: (&net.Dialer{ 6 Timeout: 30 * time.Second, 7 KeepAlive: 30 * time.Second, 8 // DualStackは Go 1.17以降非推奨(デフォルトで有効) 9 }).DialContext, 10 TLSHandshakeTimeout: 10 * time.Second, 11 ExpectContinueTimeout: 1 * time.Second, 12} 13client := &http.Client{Transport: tr}
net.Dialer
はデフォルトでIPv4/IPv6両方へ並列接続(Happy Eyeballs v2)を実施します。従来のDualStack
フラグは非推奨になったため、特別な制御が必要な場合は'tcp4'
/'tcp6'
のネットワーク指定をご検討ください。
役割: 個々のコンピュータを識別し、最終的な宛先までデータを届けるための「住所」の役割を担う層。
Goとの関連: Goのnet.IP
型やnet.ParseIP()
といった関数により、IPアドレスを安全に扱えます。
現代のGo TCP/IP入門では、IPv4とIPv6の併用が重要です。
go1// IPv4/IPv6両対応のサーバー 2listener, err := net.Listen("tcp", ":8080") // IPv4/IPv6両方でリッスン 3if err != nil { 4 log.Fatal(err) 5} 6 7// 特定のIPバージョンを指定する場合 8listener4, err := net.Listen("tcp4", ":8080") // IPv4のみ 9listener6, err := net.Listen("tcp6", ":8080") // IPv6のみ
実運用では、IPv6が利用できない環境での適切なフォールバック処理が重要になります。
役割: google.com
のようなドメイン名を、IPアドレスに変換する、インターネットの「電話帳」です。
Goとの関連: net.LookupHost()
などの関数で、Goから直接DNSの名前解決を行えます。
現代のGo ネットワークプログラミングでは、以下のDNS技術も重要です:
go1// DNSキャッシュを意識したルックアップ 2resolver := &net.Resolver{ 3 PreferGo: true, 4 Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 5 d := net.Dialer{ 6 Timeout: time.Millisecond * 200, 7 } 8 return d.DialContext(ctx, network, "8.8.8.8:53") 9 }, 10} 11 12ips, err := resolver.LookupHost(context.Background(), "example.com") 13if err != nil { 14 // エラーハンドリング 15}
http.Get("https://example.com")
が実行される際の処理の流れを、シーケンス図で可視化します。
この図は、Goクライアントがhttp.Get()
を実行する際の6つのステップを示しています:
mermaid1sequenceDiagram 2 participant Client as Goクライアント 3 participant DNS as DNSサーバー 4 participant Server as Webサーバー 5 6 Client->>DNS: 1. example.comのIPアドレス問い合わせ 7 DNS-->>Client: IPアドレス返却 8 Client->>Server: 2. TCP接続確立 (3-way handshake) 9 Client->>Server: 3. TLSハンドシェイク 10 Client->>Server: 4. HTTP GETリクエスト送信 11 Server-->>Client: 5. HTTPレスポンス返却 12 Client->>Server: 6. TCP接続クローズ (Keep-Alive時は再利用)
詳細な処理ステップ:
example.com
のIPアドレスを問い合わせるgo1transport := &http.Transport{ 2 MaxIdleConns: 100, // 全体のアイドル接続数 3 MaxIdleConnsPerHost: 10, // ホスト毎のアイドル接続数 4 IdleConnTimeout: 90 * time.Second, 5 // 注意:HTTP/2では設定項目が異なります 6}
注意: HTTP/2接続では、コネクションプールの動作が異なります。HTTP/2は単一接続で多重化を行うため、MaxIdleConnsPerHost
の効果が限定的になる場合があります。
go1client := &http.Client{ 2 Timeout: 30 * time.Second, // リクエスト全体のタイムアウト 3 Transport: &http.Transport{ 4 DialContext: (&net.Dialer{ 5 Timeout: 5 * time.Second, // TCP接続タイムアウト 6 }).DialContext, 7 TLSHandshakeTimeout: 10 * time.Second, // TLSハンドシェイクタイムアウト 8 ResponseHeaderTimeout: 10 * time.Second, // レスポンスヘッダータイムアウト 9 }, 10}
go1// カスタムDialerでDNSキャッシュを活用 2dialer := &net.Dialer{ 3 Timeout: 5 * time.Second, 4 KeepAlive: 30 * time.Second, 5 // DualStackは非推奨(Go 1.17以降デフォルトで有効) 6}
普段使っているnet/http
パッケージが、DNS、TCP/IPといった技術の層の上に成り立っていることを理解することは、単なる知識の蓄積ではありません。
このGo ネットワーク基礎知識を持つことで、パフォーマンス問題のボトルネックがどこにあるのか、より深いレベルでのトラブルシューティングが可能になります。これが、単なるライブラリの利用者から、システムの挙動を本質的に理解するエンジニアへと成長するための鍵です。
http.Transport
設定を見直すtime
パッケージで計測してみる私たちGoForceはGo言語のスキルだけでなく、その土台となるコンピュータサイエンスの基礎知識を深く理解しているプロフェッショナルなエンジニアを高く評価しています。あなたのその「基礎体力」と「問題解決能力」は、クライアントが直面する最も困難な課題を解決する上で、大きな力となります。ぜひ一度、私たちにご相談ください。
最適なGo案件を今すぐチェック!