1. ホーム
  2. python

[解決済み] 接続拒否や接続タイムアウトの原因をデバッグするにはどうすればよいですか?

2022-02-19 15:53:01

質問内容

私は約1年間動作している次のコードを持っています。

import urllib2

req = urllib2.Request('https://somewhere.com','<Request></Request>')
data = urllib2.urlopen(req)
print data.read()

最近、ランダムにエラーが発生することがあります。

  • urllib2.URLError: <urlopen error [Errno 111] Connection refused>
  • <urlopen error [Errno 110] Connection timed out>

失敗の痕跡は

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    data = urllib2.urlopen(req).read()
  File "/usr/lib/python2.7/urllib2.py", line 126, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 400, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 418, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1215, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1177, in do_open
    raise URLError(err)
urllib2.URLError: <urlopen error [Errno 111] Connection refused>

上記のエラーはランダムに発生し、1回目の実行は成功しても、2回目の実行で失敗したり、その逆もありえます。

デバッグして問題の原因を突き止めるには、どうすればよいですか? エンドポイントが私のリクエストを消費してレスポンスを返したが、私に届かなかった場合、どのように見分ければよいのでしょうか?

telnetを使用する場合

telnetでテストしたところ、Pythonと同じように成功するときとしないときがありました。

成功時。

$ telnet somewhere.com 443
Trying XXX.YY.ZZZ.WWW...
Connected to somewhere.com.
Escape character is '^]'.
Connection closed by foreign host.

接続が拒否された場合。

$ telnet somewhere.com 443
Trying XXX.YY.ZZZ.WWW...
telnet: Unable to connect to remote host: Connection refused

タイムアウト時

$ telnet somewhere.com 443
Trying XXX.YY.ZZZ.WWW...
telnet: Unable to connect to remote host: Connection timed out

解決方法は?

問題点

問題はネットワーク層にあります。以下はステータスコードの説明です。

  • Connection refused : ピアは、それぞれでリッスンしていません ネットワークポート に接続しようとしている。これは通常、ファイアウォールが積極的に接続を拒否しているか、相手側のサイトでそれぞれのサービスが起動していないか、過負荷になっていることを意味します。

  • Connection timed out : TCP接続の確立を試みている間、相手側から与えられた制限時間内に応答が来なかった。urllibの文脈では、これは かもしれない は、HTTPレスポンスが時間内に到着しなかったことを意味します。これはファイアウォールが原因のこともありますし、ネットワークの混雑やリモート (あるいはローカル) サイトの高負荷が原因のこともあります。

コンテキスト

つまり、あなたのスクリプトに問題があるのではなく、リモートサイトに問題があるのだろうということです。時々発生するようであれば、相手サイトの負荷に問題があるか、相手サイトへのネットワーク経路が不安定であることを示しています。

また、ネットワークの問題なので、相手側で何が起こったかはわかりません。一方向には問題なくパケットが飛んでいるのに、もう一方では落とされている(あるいは経路がずれている)可能性があります。

また、(直接の)DNSの問題でもなく、その場合は別のエラー( 名前またはサービスがわからない またはそれに類するもの)。しかし、DNSがリクエストごとに異なるIPアドレスを返すように設定されている場合、接続を試みるたびに異なるアドレスのホストに接続してしまう(DNSキャッシュは別として)可能性があります。また、これらのホストが誤って設定されていたり、過負荷になっているために、前述のような問題が発生することもあります。

このデバッグ

他の回答で提案されているように、パケットアナライザを使用すると、問題をデバッグするのに役立ちます。しかし、エラーメッセージの内容を正確に反映したパケット以外、あまり見ることはできません。

ネットワークの輻輳を問題として除外するには、次のようなツールを使用することができます。 mtr または traceroute あるいは ping を使用して、リモートサイトへのパケットが失われるかどうかを確認します(以下を参照してください)。

ネットワークの混雑が問題でない場合(つまり、パケットの1%以上が失われない場合)、リモートサーバーの管理者に連絡して、何が問題なのかを把握する必要があります。管理者は、システムログから関連情報を得ることができるかもしれません。また、リモートサーバーでパケットアナライザーを実行すると、ローカルサイトよりも明らかになるかもしれません。ポートが開いているかどうかを確認するには netstat -tlp をお勧めします。

tracerouteの結果を解釈する

中間ホップでの高いレイテンシや損失は、すべてを意味することもあれば、何も意味しないこともあるため、これにはある程度の練習が必要です。

中間ホップは通常、インターネットやISPのネットワークにある大きなルーターで、多くのパケットを処理します。彼らはあなたのtracerouteに返信するよりも良いことを持っているかもしれませんので、彼らは現在非常に忙しい場合は、リクエストの10%にのみ返信することを選択するかもしれません。あるいは、全く返信しないことを選ぶかもしれません。もし、あなたの最終ホップでロスが見られないのであれば、おそらくロスに関しては問題ないでしょう。

しかし、もしあなたが する 最後のホップで損失が発生した場合、そのパケットが本当に最後のホップで損失したのかどうか、確認することはできません。中間ホップのいずれかが原因である可能性があります。一般的に、それ以前のホップでも損失が見られるので、本当の送信元を示すかもしれません。

さらに追い打ちをかけるように、表示されているルートが本当のルートでない可能性もあります。本当のルートは、非対称である可能性があります。 から 目的地(tracerouteで表示されるもの)と返信(tracerouteの動作により表示されないもの)とは異なる経路をとります。

まとめると

  • tracerouteで観察される損失は、あなたが見ているホップと同じかそれ以前のホップによってのみ引き起こされる可能性があります。
  • エンドツーエンドの損失がない中間ホップでの損失は、単にそのホップが返信するのを面倒くさがったということかもしれません。
  • フォワードパス(tracerouteで見るもの)がリバースパスと不等間隔である場合があります。
  • ルートの途中から始まる部分的な損失(1%~90%)は、通常、ネットワークの輻輳を示します。通常、これに対して何かをすることはできません。