zoukankan      html  css  js  c++  java
  • 测试出真知

    这段时间一直在考虑替换ip库。

    公司的业务中需要对ip归属地的准确性要求很高,之前的ip网段表已经不能满足日渐庞大的全国终端的ip检测。这个工作交到我手里后,leader让我去解析dat出ip段,方便入库查询,也是走之前查表的老路。

    我拿到开源的dat和php接口文件,仔细看了一下解析代码,发现并不是很规范的解析,也不能直接套用qq纯真ip的dat的解析方式。不能完全解析,那么就不能弄出ip段,一时我陷入了困局。我没忘记和耀哥讨论,他提醒了我:为何一定要入库,对方提供的dat为二进制文件,直接利用接口查询,里面的索引足够快了。

    入库的好处在于可以支持其他复杂查询,但是目前来看,这里只是做根据某个ip查出归属地的要求,接口解析dat足够了。鉴于之前的经验,我在需求讨论会议里面提出了自己的想法,然后分步骤去完成这个方案。

    在讨论的过程里面,我从需求本质出发,首先要确定这个开源ip库的数据是否准确,然后再是采用入库还是dat,或者后期加缓存来做。

    我先用所有用户登录的log表里面ip去遍历走开源接口读取dat,每次循环中我同时也查询138(百度ip库)接口的数据,两者写入txt文件,以逗号分隔,方便转为csv查看结果。关键性代码如下        

             // 所有符合条件的IP
            $ipArr = $this->db->select($sql);
            $all_ips = array();
            foreach ($ipArr as $k) {
                array_push($all_ips, $k['soft_last_login_ip']);
            }
    
            if (!empty($exist_ips)) {
                $exist_ip_arr = array();
                foreach ($exist_ips as $k) {
                    $exist_ip_arr[] = $k['ip'];
                }
               $all_ips = array_diff($all_ips, $exist_ip_arr);
            }
    
            $all_ips = array_filter($all_ips); // clean out all
    
            if (empty($all_ips)) exit('已经没有ip需要入库了');
    
            $ipUrl = 'http://www.xxxx.com/index.html'; // xxxx的查询地址
            $url = 'http://www.ip138.com/ips138.asp'; // 138baidu的查询地址
            $post_data = array();
    
            //var_dump($ipArr);die();
            foreach ($all_ips as $i) {
                $ip = $i;
                $post_data['ip'] = $ip;
                $res = $this->request_post($ipUrl, $post_data);
                $addressInfo = $this->matchAddress($res);
                $address = trim($addressInfo[0]);
    
                $response = sendRequest($url, array('ip' => $ip, 'action' => 2));
                $response = mb_convert_encoding($response, "UTF-8", "gb2312");
    
                $zhengze = '/<li>本站主数据:(.*?)</li>/';
                preg_match_all($zhengze, $response, $result);
    
                if (count($result[1]) > 0) {
                   // some codes to make the data sctruct
    $this->db->insert('ip_ip_test', $row, true); usleep(mt_rand(100000, 2000000)); } }

    其中 request_post func和sendRequest是curl方式发送request获取资源内容,后面matchAddress就是利用正则匹配出归属地字符串,这些知识点在之前的一篇聊php curl模拟请求的blog里面有写,不再赘述。

    不过其中我在做curl模拟发请求的时候,发现有些网站对模拟出来的请求会报403 forbidden,这个需要仔细排查,是不是自己的cookie数据没有加入到请求里面,还是referer没有指定。最好是把header中的referer设置为原网站的某个站内链接。

    在爬数据的时候,我还遇到个问题,有些数据是页面加载后发ajax请求获得的,我到现在写这篇文章之时也没想到能抓取页面ajax之后的数据的方法。

    之后我对比了开源库和138的ip归属地,基本一致。于是和公司同事讨论,决定用这个开源的ip库,并后期使用高级版。

    那么关于数据来源的处理问题又来了,到底是入库,还是直接用dat文件。首先dat文件有几种更新周期,公司预计会使用一周更新一次的版本。如果入库,可以批量初始化写入mysql,当每周更新的时候再通过脚本批量更新database。目前ip数目大概在30万左右,更新也只需要10到20分钟之间,因为做了主从,所以也不用担心读写压力。我思考了一下,根据业务,肯定是查询频率远大于写入,所以建存储的表用myisam.

    如果是dat,它本来就是一个数据库,直接替换原来的查询来源即可,修改的地方集中在checkIp这个func上。还要考虑异地机房网络消耗和并发情况,做一个需求得全面思考。

    leader这次协助我一起思考方案,决定把每一种方式都写一个测试接口,利用高并发压力测试来看哪一种方式的性能更好。

    于是我写好了三个测试接口:读取mysql,读取dat,读取redis(初始化写入了内存)的方式。

    利用压测工具webbench,逐步测试200个并发,1000个并发,10000个并发条件下的吞吐读取速度。

    其中dat的某次结果如下:

    webbench -t 50 -c 10000 http://interface.xxx.com/test/testGetIPByDat
    Webbench - Simple Web Benchmark 1.5
    Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.

    Benchmarking: GET http://interface.xxx.com/test/testGetIPByDat
    10000 clients, running 50 sec.

    Speed=337046 pages/min, 1979493 bytes/sec.
    Requests: 280872 susceed, 0 failed.

    测试出真知,速度由快到慢,最终结果为:redis > dat > mysql。 dat的速度比redis差,但是如果直接使用它不用每周还手动去更新redis里面的key,成本最低。

    一步一步,数据为王,测试为证。虽然这次花时间不少,不过也收获了严谨求实的做事方式,真正的需求和解决方案是讨论出来的。感谢所有帮助我的人,leader,同事,耀哥。我的每一次进步,都心存感激。

  • 相关阅读:
    Java代理(静态/动态 JDK,cglib)
    Java数据库基础(JDBC)
    Servlet基础(工作原理、生命周期)
    Java XML DOM解析(xPath)
    java 文件操作
    从源码看集合ArrayList
    全面理解java异常机制
    python3 利用pip安装ipython notebook
    Centos的一个find命令配合rm删除某天前的文件
    在Pandas中直接加载MongoDB的数据
  • 原文地址:https://www.cnblogs.com/freephp/p/4962053.html
Copyright © 2011-2022 走看看