[PerlChina] PerlChina Advent Day 19: 客户端来源 IP 信息处理

5 views
Skip to first unread message

chenlin rao

unread,
Dec 19, 2014, 10:05:13 AM12/19/14
to perl...@googlegroups.com
=encoding utf8

=for advent_year 2014

=for advent_title 客户端来源 IP 信息处理

=for advent_author fukai

=head2 前言

我们进行日志处理, 常有的一个需求就是需要日志中来源的 IP 变成指定的信息来进行分析, 也有可能有一种需求, 就是需要给所有用户的请求转换成 "ISP, 省市, 城市", 然后在排序分析用户来源相关的信息, 如省市排名, 运营商排名, 更加复杂的还有可能, 根据这些信息来做基础信息来判断用户的体验, 比如那个地区, 那个 ISP 的用户访问速度最快等等.
另外, 对于象视频, 大文件下载, 这种应用, 目前很多是基于 HTTP 之类的更加高级的调度, 这时我们就需要更加给请求过来的用户 IP , 也转换成相应的地区, 然后查询自己的全网调度系统中, 是否存在相应最优节点.

基于以上二点, 我们需要非常非常快速的 IP 转换成城市信息的模块. 在 08 年的时候, 我就想过各种方式, 使用过 Geo::IP 之类的库, 当年的 QQway.bat 的结构,还有比如给 IP 段折开, 存到数据之类的地方, 然后有了 IP , 根据这个信息来查询段是什么 ISP 和地区, 试过各种方式, 但速度和效率都不太理想.

比如日志 cat 出来后进行二次分析和清洗的时候, 这个查询和转换是最占用时间的, 真到我找到了 Net::IP::Match::Trie 这个模块, 这个速度快到可以不影响你 cat 的速度, 直接线性的处理 IP 到信息的转换.

当年这个模块还是刚出来的时候我关注到的, 非常好用, 非常想推荐给其它人. 但一直没有发布正式版本, 就这样用了几年, 因为不是正式版本, 所以 cpanm 一直找不到这个模块, 要指定 url 才能安装. 直到去年末, 我发了个邮件给作者, 提醒他, 这个模块几年还没 release , 作者接到邮件后立即就更新, 所以现在正式接这个机会推荐给有相应需求的朋友 ( 注: cpan 作者都非常热情, 有问题直接联系作者哦 ). 注: 我自己的调度系统中一直使用这个模块, 很多年了.

=head2 原始 IP 段和地址信息的加入

进行 IP 信息处理, 我们需要先收集 IP 段是哪个运营商. 目前网络上这种数据很多, 可以下载任何一个数据来加入到这个模块当中.

  use Net::IP::Match::Trie;
  my $matcher = Net::IP::Match::Trie->new;
  $matcher->add(CNC-ZJ-ZJ => [qw(66.249.64.0/19 74.125.0.0/16)]);
  $matcher->add(CNC-ZJ-ZJ  => [qw(69.147.64.0/18 209.191.64.0/18 209.131.32.0/19)]);


我是直接给对应的信息写成了一个如下的文件结构, 然后使用上面的方式导入.


上面 add 的 key 就是后面的 CNC,ZJ-ZJ. 然后在加上网段.


=head2 来源 IP 的查询

查询非常简单, 只需要给 IP 放到 match_ip 的方法中就搞定了.

  say $matcher->match_ip("66.249.64.1"); # => "CNC-ZJ-ZJ"


=head2 样例

下面是一个简单的管道传送过来的日志进行 IP 地址翻译的小程序.

  use strict;
  use 5.010;

  use Net::IP::Match::Trie;
  my $matcher = Net::IP::Match::Trie->new;

  open my $fh, "<", 'isp_ip.csv' or die "print $!";
  while (<$fh>) {
      my ($CIDRS, $isp, $region) = split(/,/, $_);
      chomp $region;
      my $name  = "$isp-$region";
      $matcher->add( $name , [$CIDRS] );
  }

  while(<>){
      chomp;
      my $ISP = $matcher->match_ip($_);
      print "$_, $ISP\n";
  }

=head2 使用

象上面, 可以用于日常的日志处理, 让信息读进来一次, 给 IP 以流的方式 cat 进去就好了, 象日志处理什么之类就非常好用了.

  # echo 202.106.0.20 |perl check.pl
  202.106.0.20, CNC-BJ-BJ

=head2 IP 段的整理

象上面的 IP 段, 因为运营商信息不断的变化, 所以你的 IP 数据库也会跟着不断的变化, 当运营商给你一堆 IP 时, 你对很多重复的段要合并, 有些小的段要折分, 这样手工操作很累, 同样, 也推荐一个模块, 叫 Net::IP::RangeCompare , 小小包装和处理一下, 配合起来使用, 非常合适.

=head2 作者

fukai, L<mail://ia...@163.com>

=cut


Reply all
Reply to author
Forward
0 new messages