在运维工作中我们经常需要检测用户访问是否正常,一般来说,直接通过 DNS 客户端获取 A 记录就可以满足需要。不过如果我们可以获得具体连接的 IP 地址,那么就可以缩小问题的判断范围,因为 DNS 的 A 记录通常是有多个的。

AE::HTTP 模块可以返回 sock 给用户进行具体操作,我们可以通过 sock 接口很简单的获得对端的 IP 地址:

package Web::Checker::Util::HTTP;
use Moo;
use MooX::Types::MooseLike::Base qw/Str Num/;
use AnyEvent::HTTP;
use AnyEvent::Socket;
use AnyEvent;
use Time::HiRes qw/time/;

has peer    => ( is => 'rw', isa => Str );
has reptime => ( is => 'rw', isa => Num );
has clength => ( is => 'rw', isa => Num );
has body    => ( is => 'ro', isa => Str );
has proxy   => ( is => 'ro', isa => Str, default => sub { undef } );
has cv => ( is => 'ro', default => sub { AnyEvent->condvar } );

sub get {
    my ( $self, $url ) = @_;
    $self->cv->begin;
    my $begin = time;
    http_get $url,
      proxy            => $self->proxy,
      # 就是这里发挥了作用,默认应该是直接返回 body 字符串的
      want_body_handle => 1,
      sub {
        my ( $hdl, $headers ) = @_;
        my ( $port, $peer ) =
          AnyEvent::Socket::unpack_sockaddr getpeername $hdl->{fh};
        $self->peer( AnyEvent::Socket::format_address $peer );
        if ( $headers->{Status} =~ /^2/ ) {
            my $end = time;
            $self->reptime( $end - $begin );
            $self->clength( $headers->{'content-length'} );
            $self->cv->end;
        }
      };
    $self->cv->recv;
}

1;

其实 AE::HTTP 还可以在 tcp_connect 的时候获取 sock,这时候就需要自己用 AnyEvent::Handle 写一遍 AnyEvent::HTTP::tcp_connect 已经写过的东西了(当然如果你本来就打算干点别的事情,那就是另外一回事情了)~~