Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12776] ファイルの概要

このコミットは、Go言語のnetパッケージにおいて、syscall.Accept呼び出し時に発生するECONNABORTEDエラーを適切に無視するよう修正するものです。これにより、ネットワーク接続が確立される前にクライアントによって切断された場合に発生する、無害なエラーによってサーバーの処理が中断されるのを防ぎます。

コミット

commit a63c37b91e1483e10e073609a75e077ef1c6c827
Author: Devon H. O'Dell <devon.odell@gmail.com>
Date:   Tue Mar 27 00:06:14 2012 -0400

    net: ignore ECONNABORTED from syscall.Accept
    
    Fixes #3395.
    
    R=rsc, dsymonds
    CC=golang-dev
    https://golang.org/cl/5905063

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a63c37b91e1483e10e073609a75e077ef1c6c827

元コミット内容

net: ignore ECONNABORTED from syscall.Accept

Fixes #3395.

R=rsc, dsymonds
CC=golang-dev
https://golang.org/cl/5905063

変更の背景

この変更は、Go言語のネットワークパッケージがsyscall.AcceptシステムコールからECONNABORTEDエラーを受け取った際の挙動を改善するために行われました。ECONNABORTEDエラーは、「ソフトウェアが接続を中断した」ことを意味し、クライアントが接続を確立する前に切断した場合に発生します。これは、サーバーがAccept()を呼び出す前に、リスンキュー上のソケットが閉じられた場合に起こり得る、比較的無害なエラーです。

このエラーが適切に処理されないと、サーバーアプリケーションが不必要にエラーを報告したり、場合によっては接続処理が中断されたりする可能性があります。特に、高負荷な環境や不安定なネットワーク環境では、このような一時的な接続中断は頻繁に発生し得るため、サーバーの堅牢性を高める上でこのエラーを適切に無視し、再試行するロジックが必要とされました。この修正は、GoのIssue #3395で報告された問題に対応するものです。

前提知識の解説

  • syscall.Accept: オペレーティングシステムが提供するシステムコールの一つで、サーバーがクライアントからの新しい接続要求を受け入れるために使用されます。TCP/IPソケットプログラミングにおいて、サーバーはまず特定のポートで接続を「リスン」し、その後Acceptを呼び出して、リスンキューに並んだ接続要求を一つずつ処理し、新しい接続済みソケットを生成します。
  • ECONNABORTED: POSIXシステムコールで定義されているエラーコードの一つです。errno.hで定義されており、通常は「Software caused connection abort」(ソフトウェアが接続を中断した)を意味します。ネットワークプログラミングの文脈では、クライアントが接続を確立する前に切断した場合(例えば、クライアントが接続要求を送った直後にクラッシュしたり、タイムアウトしたりした場合)に、サーバー側のAccept呼び出しがこのエラーを返すことがあります。これは、ECONNRESET(接続がピアによってリセットされた)とは異なり、接続確立フェーズで発生するものです。
  • TCP/IP接続の確立 (3-way handshake): TCP接続は、クライアントとサーバー間で3段階のハンドシェイク(SYN, SYN-ACK, ACK)を経て確立されます。
    1. クライアントがサーバーにSYN (Synchronize) パケットを送信。
    2. サーバーがSYNを受信し、SYN-ACK (Synchronize-Acknowledge) パケットをクライアントに送信。
    3. クライアントがSYN-ACKを受信し、ACK (Acknowledge) パケットをサーバーに送信。 このハンドシェイクが完了する前に、クライアントが接続を中断すると、サーバー側でECONNABORTEDエラーが発生する可能性があります。
  • Go言語のnetパッケージ: Go言語の標準ライブラリの一部で、ネットワークI/O機能を提供します。TCP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うための抽象化されたインターフェースを提供し、低レベルのsyscallパッケージをラップして、よりGoらしいエラーハンドリングと並行処理を可能にしています。

技術的詳細

syscall.AcceptECONNABORTEDを返す状況は、通常、リスンキューに接続要求が到着したが、サーバーがその接続をAcceptする前にクライアント側が接続を閉じてしまった場合に発生します。これは、TCPの3ウェイハンドシェイクが完了する前に、クライアントが接続試行を中止したことを意味します。

多くのオペレーティングシステムでは、このECONNABORTEDエラーは、サーバーが新しい接続を受け入れる準備ができていたにもかかわらず、外部要因(クライアントの切断)によってその試みが中断されたことを示すものです。このエラーは、サーバーの動作自体に問題があるわけではなく、単にその特定の接続試行が成功しなかったことを示唆しています。したがって、このようなエラーが発生した場合、サーバーは通常、次の接続要求を処理するためにAccept呼び出しを再試行すべきです。

このコミットでは、Goのnetパッケージ内のfd.goファイルにあるacceptメソッドが修正されています。このメソッドは、低レベルのsyscall.Acceptを呼び出し、その結果を処理します。修正前は、ECONNABORTEDエラーが発生した場合、他の致命的なエラーと同様に処理され、OpErrorとして返されていました。これは、サーバーアプリケーションがこのエラーを捕捉し、適切に処理する必要があることを意味します。

しかし、ECONNABORTEDは多くの場合、無視して再試行すべきエラーであるため、このコミットでは、syscall.ECONNABORTEDが返された場合に、エラーを返さずにループの先頭に戻り、Acceptを再試行するcontinueステートメントが追加されました。これにより、Goのネットワークスタックは、このような一時的な接続中断を透過的に処理し、アプリケーション層に不必要なエラーを伝播させなくなります。結果として、Goで書かれたネットワークサーバーは、より堅牢になり、一時的なネットワークの問題やクライアントの挙動に影響されにくくなります。

コアとなるコードの変更箇所

--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -623,6 +623,10 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
 						continue
 					}
 				}
+			} else if err == syscall.ECONNABORTED {
+				// This means that a socket on the listen queue was closed
+				// before we Accept()ed it; it's a silly error, so try again.
+				continue
 			}
 			return nil, &OpError{"accept", fd.net, fd.laddr, err}
 		}

コアとなるコードの解説

変更はsrc/pkg/net/fd.goファイルの(*netFD).acceptメソッド内で行われています。このメソッドは、新しいネットワーク接続を受け入れるための主要なロジックを含んでいます。

既存のコードでは、Acceptシステムコールがエラーを返した場合、そのエラーがsyscall.EINTR(システムコールが中断された)であるかどうかをチェックし、そうであればcontinueしてループを再開していました。これは、シグナルなどによってシステムコールが中断された場合に、処理を再試行するための標準的なパターンです。

このコミットによって追加された行は以下の通りです。

+			} else if err == syscall.ECONNABORTED {
+				// This means that a socket on the listen queue was closed
+				// before we Accept()ed it; it's a silly error, so try again.
+				continue

このコードブロックは、Acceptシステムコールが返したエラーがsyscall.ECONNABORTEDである場合に実行されます。

  • else if err == syscall.ECONNABORTED: Acceptが返したエラーがECONNABORTEDであるかをチェックします。
  • // This means that a socket on the listen queue was closed // before we Accept()ed it; it's a silly error, so try again.: このコメントは、ECONNABORTEDエラーが、リスンキュー上のソケットがAcceptされる前に閉じられたことを意味し、これは「馬鹿げた(silly)」エラーであるため、再試行すべきであるという背景を説明しています。
  • continue: このキーワードは、現在のループの残りの処理をスキップし、次のイテレーションに進むことを意味します。つまり、ECONNABORTEDエラーが発生した場合、acceptメソッドはエラーを呼び出し元に返さずに、すぐにAcceptシステムコールを再試行します。

この変更により、ECONNABORTEDエラーは、Goのnetパッケージの内部で透過的に処理され、アプリケーション開発者がこの特定のエラーを明示的にハンドリングする必要がなくなりました。これにより、Goのネットワークサーバーは、一時的な接続中断に対してより堅牢で回復力のあるものになります。

関連リンク

参考にした情報源リンク