Goの「broken pipe」を追いかける(*net.OpError版)


これは TECHSCORE Advent Calendar 2017の5日目の記事です。

はじめに

今回はGoで「broken pipe」のエラーを判定して処理するようにした、という話です。

「golang broken pipe」で検索するととても素敵な記事が見つかりました。とても参考になりました。

Go で "broken pipe" を無視したい僕達がハマる罠

背景

とあるGoで作ったWebアプリケーションのエラーメッセージが出ていました。

エラーはレスポンスを返すところで起きているようです。

「broken pipe」でGoのソースコードを調べると、EPIPEのエラーメッセージであることがわかりました。
私の環境(Mac OS:darwin, ARCH:amd64)の場合は、以下で定義されています。

syscall/zerrors_darwin_amd64.go#L1314

syscall/zerrors_darwin_amd64.go#L1217

試してみる

エラーを再現するために、以下のようなクライアントとサーバーを用意します。
クライアントは、リクエストを1秒でタイムアウトするようにしています。

用意したクライアントからサーバーに向けてリクエストを投げると、「broken pipe」エラーがでます。

Goではerrorインターフェースというのがあり、エラーメッセージを表示するだけであればerrorの型を気にせずに処理を書けるようになっています。

しかし、今回のように特定のエラーのみ別の処理を当てたい場合には、errorを型アサーションしてチェックします。参考: Error handling and Go - The Go Blog

ということで、errorの型を調べてみます。ついでに構造も調べます。

結果は以下の通りです。

これで*net.OpErrorというのがわかりました。

ここでさらに、*os.SyscallErrorがでてきました。

というわけで、2段階でアサーションして、syscall.EPIPEか判定をしたコードが以下です。
これで当初の目的だった「EPIPEに対して別の処理を当てる」ことができるようになりました。

まとめ

書き込みが始まっているタイミングでクライアントがコネクションを切ると起こってしまうんですが、
もっとうまいやりかた(検証方法と解決方法)をご存知の方は教えていただけるとうれしいです。

最近 Goならわかるシステムプログラミングを読み始めたのですが、とても勉強になるので興味がある方はぜひ読んでみてください。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です