2

Net::LDAP モジュールを使用して openldap サーバー slapd に接続する mod_perl の下で perl コードを実行しています。

次のように接続タイムアウトを設定しようとしています:

my $ldap = Net::LDAP->new($server, timeout => 120); 

しかし、slapd の負荷が高いと、約 20 秒後に接続試行がタイムアウトします。

Net::LDAP は IO::Socket と IO::Select を使用して、接続処理を実装します。具体的には、IO::Socket の次のコードです (追加のデバッグ コードを少し追加したことに注意してください)。

sub connect {
    @_ == 2 or croak 'usage: $sock->connect(NAME)';
    my $sock = shift;
    my $addr = shift;
    my $timeout = ${*$sock}{'io_socket_timeout'};
    my $err;
    my $blocking;

    my $start = scalar localtime;
    $blocking = $sock->blocking(0) if $timeout;
    if (!connect($sock, $addr)) {
    if (defined $timeout && ($!{EINPROGRESS} || $!{EWOULDBLOCK})) {
        require IO::Select;

        my $sel = new IO::Select $sock;

        undef $!;
        if (!$sel->can_write($timeout)) {
        $err = $! || (exists &Errno::ETIMEDOUT ? &Errno::ETIMEDOUT : 1);
        $@ = "connect: timeout";
        }
        elsif (!connect($sock,$addr) &&
                not ($!{EISCONN} || ($! == 10022 && $^O eq 'MSWin32'))
            ) {
        # Some systems refuse to re-connect() to
        # an already open socket and set errno to EISCONN.
        # Windows sets errno to WSAEINVAL (10022)
                my $now = scalar localtime;
        $err = $!;
        $@ = "connect: (1) $! : start = [$start], now = [$now], timeout = [$timeout] : " . Dumper(\%!);
        }
    }
        elsif ($blocking || !($!{EINPROGRESS} || $!{EWOULDBLOCK}))  {
        $err = $!;
        $@ = "connect: (2) $!";
    }
    }

    $sock->blocking(1) if $blocking;

    $! = $err if $err;

    $err ? undef : $sock;
}

次のようなログ出力が表示されます。

connect: (1) Connection timed out : start = [Tue Jun 19 14:57:44 2012], now = [Tue Jun 19 14:58:05 2012], timeout = [120] : $VAR1 = {
          'EBADR' => 0,
          'ENOMSG' => 0,
<snipped>
          'ESOCKTNOSUPPORT' => 0,
          'ETIMEDOUT' => 110,
          'ENXIO' => 0,
          'ETXTBSY' => 0,
          'ENODEV' => 0,
          'EMLINK' => 0,
          'ECHILD' => 0,
          'EHOSTUNREACH' => 0,
          'EREMCHG' => 0,
          'ENOTEMPTY' => 0
        };
 : Started attempt at Tue Jun 19 14:57:44 2012

20 秒の接続タイムアウトはどこから来るのですか?

編集: 私は今犯人を見つけました: /proc/sys/net/ipv4/tcp_syn_retries、デフォルトで 5 に設定され、5 回の再試行には約 20 秒かかります。 http://www.sekuda.com/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout

4

1 に答える 1

2

更新: 一部のカーネルはそのようなものです

簡単に言えば、一部の Linux カーネルは connect() に 20 秒のタイムアウトを課しているということです。 これはバグです。

リンクされたsekudaは明らかにあいまいであることに注意してください。デフォルト値tcp_syn_retries(5) と再試行バックオフでは、20 秒をはるかに超えるタイムアウトが発生します。欠落しているニュアンスは、上記のリンクにあるバグ ディスカッションに記載されています。

元の答え

アップグレードしてみてください。

connectIO::Socket バージョン 1.34 (たとえば、perl 5.16)のサブはselect()、書き込み用エラー用のソケットです。エラーが発生したソケットは、getsockopt()/SO_ERROR を使用して検査され、真のエラー状態が確認されます。

TCP の「ソフト エラー」が発生していると思われます(たとえば、ICMP ホストに時々到達できないなど)。ただし、お使いのバージョンの IO::Socket は、SO_ERROR を確認しないため、要点を見逃しています。

アップグレードしても問題が解決しない場合、適切な修正は、IO::Socket::connect 内のロジックを釘付けにして、Linux の connect(2) のマニュアル ページで提案されていることを実行することです。書き込み可能ですconnect()select()

安価な回避策

その間、何か...

# untested!
use Errno;

...

my $relative_to = 120; 
my $absolute_to = time() + $relative_to;

TRYCONN: {
  $ldap = Net::LDAP->new($server, timeout => $relative_to);
  if (! $ldap and $!{ETIMEDOUT}) {
    $rel_to = $absolute_to - time();
    redo TRYCONN if $relative_to > 0;
  }
}

die "Aaaaargh" unless $ldap;

...または同様の方法でうまくいくはずです。

于 2012-06-19T19:05:45.433 に答える