[インデックス 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)を経て確立されます。
- クライアントがサーバーにSYN (Synchronize) パケットを送信。
- サーバーがSYNを受信し、SYN-ACK (Synchronize-Acknowledge) パケットをクライアントに送信。
- クライアントがSYN-ACKを受信し、ACK (Acknowledge) パケットをサーバーに送信。
このハンドシェイクが完了する前に、クライアントが接続を中断すると、サーバー側で
ECONNABORTED
エラーが発生する可能性があります。
- Go言語の
net
パッケージ: Go言語の標準ライブラリの一部で、ネットワークI/O機能を提供します。TCP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うための抽象化されたインターフェースを提供し、低レベルのsyscall
パッケージをラップして、よりGoらしいエラーハンドリングと並行処理を可能にしています。
技術的詳細
syscall.Accept
がECONNABORTED
を返す状況は、通常、リスンキューに接続要求が到着したが、サーバーがその接続を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のネットワークサーバーは、一時的な接続中断に対してより堅牢で回復力のあるものになります。
関連リンク
- Go Issue #3395: https://code.google.com/p/go/issues/detail?id=3395 (現在はGitHubに移行済み)
- Go CL 5905063: https://golang.org/cl/5905063
参考にした情報源リンク
ECONNABORTED
error in Go: https://medium.com/@vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHaO1yLMBnOeG5A2dbijkH3NDKvLYAexetgwimTznJZr9q6lttim_ttz70AKR0IWaX59MjVtXdXJiFmG7383oBkA_Lh24dx7eOUK4rqlYvQ-5kt75JP3aUY8waPBOxjFsSXANgw8BIX71pIl6y0yJOxe_rhUIT9YlrqrlH8iqg035lcmopOizcgrRXXDlH2fdmgRMhKfOBLiPnCaBGYmkxfuW_qAVicIUhGOhYR5pI0hA==ECONNABORTED
vsECONNRESET
: https://gosamples.dev/go-tcp-errors/man accept
: https://man7.org/linux/man-pages/man2/accept.2.html- Go
net
package source code: https://github.com/golang/go/blob/master/src/net/fd.go (現在の最新版)