zoukankan      html  css  js  c++  java
  • PHP Swoole 基于纯真IP库根据IP匹配城市

    把纯真IP库读到内存,纯真IP库本来就是有序的,然后每次请求二分查找就行,44WIP查找十几次就搞定了

    dispatch_mode最好写3,不然做服务的时候,会导致进程任务分配不均匀。

    max_request 处理请求数量累加到达该值会重启处理进程,防止内存泄露

    worker_num 根据内存和服务处理能力可以自己设置跑几个工作进程。

    swoole.php

    <?php
    require 'ipmatch.php';
    class IpServer
    {
        protected $iptables;
    
        protected static $cityList = array (
            0 =>
                array (
                    'bj' => '北京',
                ),
            1 =>
                array (
                    'sh' => '上海',
                ),
            2 =>
                array (
                    'tj' => '天津',
                ),
            3 =>
                array (
                    'cq' => '重庆',
                ),
            4 =>
                array (
                    'gz' => '广州',
                    'sz' => '深圳',
                    'dg' => '东莞',
                    'zhuhai' => '珠海',
                    'shantou' => '汕头',
                    'foshan' => '佛山',
                    'jiangmen' => '江门',
                    'zhongshan' => '中山',
                    'huizhou' => '惠州',
                    'maoming' => '茂名',
                    'shaoguan' => '韶关',
                    'zhanjiang' => '湛江',
                ),
            5 =>
                array (
                    'cd' => '成都',
                    'zigong' => '自贡',
                    'luzhou' => '泸州',
                    'deyang' => '德阳',
                    'mianyang' => '绵阳',
                    'nanchong' => '南充',
                    'liangshan' => '凉山',
                ),
            6 =>
                array (
                    'hz' => '杭州',
                    'nb' => '宁波',
                    'wenzhou' => '温州',
                    'jiaxing' => '嘉兴',
                ),
            7 =>
                array (
                    'gy' => '贵阳',
                    'liupanshui' => '六盘水',
                    'zunyi' => '遵义',
                ),
            8 =>
                array (
                    'sy' => '沈阳',
                    'dl' => '大连',
                    'anshan' => '鞍山',
                    'fushun' => '抚顺',
                ),
            9 =>
                array (
                    'nj' => '南京',
                    'su' => '苏州',
                    'wx' => '无锡',
                    'xuzhou' => '徐州',
                ),
            10 =>
                array (
                    'fz' => '福州',
                    'xm' => '厦门',
                    'putian' => '莆田',
                ),
            11 =>
                array (
                    'sjz' => '石家庄',
                    'tangshan' => '唐山',
                    'handan' => '邯郸',
                    'xingtai' => '邢台',
                    'baoding' => '保定',
                    'zhangjiakou' => '张家口',
                    'chengde' => '承德',
                ),
            12 =>
                array (
                    'zz' => '郑州',
                    'luoyang' => '洛阳',
                    'pingdingshan' => '平顶山',
                    'jiaozuo' => '焦作',
                    'hebi' => '鹤壁',
                    'xinxiang' => '新乡',
                    'anyang' => '安阳',
                ),
            13 =>
                array (
                    'cc' => '长春',
                    'jilin' => '吉林',
                ),
            14 =>
                array (
                    'hrb' => '哈尔滨',
                    'qiqihaer' => '齐齐哈尔',
                    'jixi' => '鸡西',
                    'hegang' => '鹤岗',
                    'shuangyashan' => '双鸭山',
                ),
            15 =>
                array (
                    'jn' => '济南',
                    'qd' => '青岛',
                    'wei' => '威海',
                    'zibo' => '淄博',
                    'zaozhuang' => '枣庄',
                    'dongying' => '东营',
                    'yantai' => '烟台',
                ),
            16 =>
                array (
                    'hf' => '合肥',
                    'wuhu' => '芜湖',
                    'bengbu' => '蚌埠',
                    'maanshan' => '马鞍山',
                    'anqing' => '安庆',
                ),
            17 =>
                array (
                    'nn' => '南宁',
                    'gl' => '桂林',
                    'liuzhou' => '柳州',
                    'wuzhou' => '梧州',
                    'qinzhou' => '钦州',
                ),
            18 =>
                array (
                    'hn' => '海口',
                    'sanya' => '三亚',
                    'wuzhishan' => '五指山',
                ),
            19 =>
                array (
                    'nmg' => '呼和浩特',
                    'baotou' => '包头',
                    'wuhai' => '乌海',
                ),
            20 =>
                array (
                    'ty' => '太原',
                    'datong' => '大同',
                    'yangquan' => '阳泉',
                    'changzhi' => '长治',
                ),
            21 =>
                array (
                    'yc' => '银川',
                    'shizuishan' => '石嘴山',
                    'wuzhong' => '吴忠',
                    'guyuan' => '固原',
                    'zhongwei' => '中卫',
                ),
            22 =>
                array (
                    'lz' => '兰州',
                    'jinchang' => '金昌',
                    'baiyin' => '白银',
                    'tianshui' => '天水',
                    'wuwei' => '武威',
                ),
            23 =>
                array (
                    'xa' => '西安',
                    'tongchuan' => '铜川',
                    'baoji' => '宝鸡',
                    'xianyang' => '咸阳',
                    'weinan' => '渭南',
                ),
            24 =>
                array (
                    'xn' => '西宁',
                    'haidong' => '海东',
                    'haibei' => '海北',
                    'huangnan' => '黄南',
                    'hainan' => '海南',
                    'guoluo' => '果洛',
                    'yushu' => '玉树',
                    'haixi' => '海西',
                ),
            25 =>
                array (
                    'wh' => '武汉',
                    'huangshi' => '黄石',
                    'xiangfan' => '襄樊',
                    'shiyan' => '十堰',
                    'jingzhou' => '荆州',
                    'yichang' => '宜昌',
                    'jingmen' => '荆门',
                ),
            26 =>
                array (
                    'cs' => '长沙',
                    'zhuzhou' => '株洲',
                    'xiangtan' => '湘潭',
                    'hengyang' => '衡阳',
                    'shaoyang' => '邵阳',
                ),
            27 =>
                array (
                    'nc' => '南昌',
                    'jingdezhen' => '景德镇',
                    'pingxiang' => '萍乡',
                    'jiujiang' => '九江',
                ),
            28 =>
                array (
                    'km' => '昆明',
                    'qujing' => '曲靖',
                    'yuxi' => '玉溪',
                ),
            29 =>
                array (
                    'xj' => '乌鲁木齐',
                    'kelamayi' => '克拉玛依',
                    'tulufan' => '吐鲁番',
                    'hami' => '哈密',
                    'hetian' => '和田',
                ),
            30 =>
                array (
                    'xz' => '拉萨',
                    'changdu' => '昌都',
                    'shannan' => '山南',
                    'rikaze' => '日喀则',
                    'naqu' => '那曲',
                    'ali' => '阿里',
                    'linzhi' => '林芝',
                ),
        );
    
        protected static $provinceList = array (
            4 => '广东',
            5 => '四川',
            6 => '浙江',
            7 => '贵州',
            8 => '辽宁',
            9 => '江苏',
            10 => '福建',
            11 => '河北',
            12 => '河南',
            13 => '吉林',
            28 => '云南',
            15 => '山东',
            16 => '安徽',
            17 => '广西',
            18 => '海南',
            19 => '内蒙古',
            20 => '山西',
            21 => '宁夏',
            22 => '甘肃',
            23 => '陕西',
            24 => '青海',
            25 => '湖北',
            26 => '湖南',
            27 => '江西',
            14 => '黑龙江',
            29 => '新疆',
            30 => '西藏',
    
        );
    
        function run()
        {
            $serv = swoole_server_create("192.168.2.165", 9898);
            swoole_server_set($serv, array(
                'worker_num' => 2,
                'max_request' => 10000,
                'dispatch_mode' => 3
            ));
            swoole_server_handler($serv, 'onWorkerStart', array($this, 'onStart'));
            swoole_server_handler($serv, 'onConnect', array($this, 'onConnect'));
            swoole_server_handler($serv, 'onReceive', array($this, 'onReceive'));
            swoole_server_handler($serv, 'onClose', array($this, 'onClose'));
            swoole_server_handler($serv, 'onWorkerStop', array($this, 'onShutdown'));
            swoole_server_start($serv);
        }
    
        public function onStart($serv)
        {
            echo 'read iptables from file'.PHP_EOL;
            $this->iptables = file('ip.txt');
        }
    
        public function onConnect($serv, $fd, $from_id)
        {
        }
    
        public function onReceive($serv, $fd, $from_id, $data)
        {
            $ret = match_ip($data, $this->iptables, self::$cityList, self::$provinceList);
            $serv->send($fd, $ret);
            $serv->close($fd);
        }
    
        public function onClose($serv, $fd, $from_id)
        {
        }
    
        public function onShutdown($serv)
        {
            echo 'shutdown server and free resource'.PHP_EOL;
            unset($this->iptables);
        }
    }
    
    $server = new IpServer();
    $server->run();

    有一点需要注意下的PHP 的整形是个有符号的Long 所以溢出时候会产生负数,需要处理一下。

    ipmatch.php

    <?php
    
    function ip2num($ip) {
        return sprintf("%u",ip2long($ip));
    }
    
    function match_ip($ip, $data, $cityList, $provinceList) {
        $count = count($data);
        $ip = ip2num($ip);
        $i=0;
        $j=$count-1;
        while ($i<=$j) {
            $mid = intval(($i+$j)/2);
            $arr = preg_split('/s+/', $data[$mid]);
            $midip = ip2num(trim($arr[0]));
            if ($midip>$ip) {
                $j = $mid-1;
            } else if ($midip<$ip) {
                $i = $mid+1;
            } else {
                break;
            }
        }
    
        $temp = preg_split('/s+/', $data[$j]);
        $beginip = ip2num(trim($temp[0]));
        $endip = ip2num(trim($temp[1]));
        if ($ip>=$beginip&&$ip<=$endip) {
            unset($temp[0]);
            unset($temp[1]);
            foreach ($temp as $t) {
                $keyword = trim($t);
                if (!empty($keyword)) {
                    break;
                }
            }
            $keyword = iconv('GBK', 'UTF-8', $keyword);
            //和省匹配
            foreach ($provinceList as $code=>$name) {
                if (strstr($keyword, $name)) {
                    $provinceName = $name;
                    $provinceCode = $code;
                    break;
                }
            }
            if (isset($provinceCode)) {
                //和省内城市匹配
                foreach ($cityList[$provinceCode] as $code=>$name) {
                    if (strstr($keyword, $name)) {
                        return '1:'.$code.':'.$name;
                    }
                }
            } else {
                //和所有城市匹配
                foreach ($cityList as $arr) {
                    foreach ($arr as $code=>$name) {
                        if (strstr($keyword, $name)) {
                            return '1:'.$code.':'.$name;
                        }
                    }
                }
            }
            if (isset($provinceCode)) {
                return '2:'.$provinceCode.':'.$provinceName;
            }
            return '-1::';
        }
    }
    
    ?>

    启动SERVER

    php swoole.php

    测试

    client.php

    class runtime
    {
        var $StartTime = 0;
        var $StopTime = 0;
    
        function get_microtime()
        {
            list($usec, $sec) = explode(' ', microtime());
            return ((float)$usec + (float)$sec);
        }
    
        function start()
        {
            $this->StartTime = $this->get_microtime();
        }
    
        function stop()
        {
            $this->StopTime = $this->get_microtime();
        }
    
        function spent()
        {
            return round(($this->StopTime - $this->StartTime) * 1000, 1);
        }
    
    }
    
    function ip2City($ip)
    {
        $fp = fsockopen("192.168.2.165",9898, $errno, $errstr, 1);
        if (!$fp) {
            echo $errno . $errstr.PHP_EOL;
        } else {
            fwrite($fp, $ip);
            $out = '';
            while (!feof($fp)) {
                $out .= fgets($fp).PHP_EOL;
            }
        }
        fclose($fp);
        return $out;
    }
    $test = array();
    $notMatch = array();
    //读文件
    $ips = file('access_wap_misc_20140115_part1.log');
    foreach ($ips as $line) {
        $ip = explode(' ', $line);
        $test[] = $ip[0];
    }
    ////随即生成
    //for ($i=0;$i<10000;$i++) {
    //    $test[] = rand(1,255).'.'.rand(1,255).'.'.rand(1,255).'.'.rand(1,255);
    //}
    ////指定IP
    //$test[] = '122.11.37.92';
    
    
    $match_count = 0;
    $runtime= new runtime;
    $runtime->start();
    foreach ($test as $ip) {
        $city = ip2City($ip);
        if (strlen($city)>1) {
    //        $arr = explode(':',$city);
            echo $ip .' locate '. $city;
            $match_count++;
        }else {
            $notMatch[] = $ip;
        }
    }
    $runtime->stop();
    echo "match ".count($test)." ips total run time: ".$runtime->spent()." milliseconds".PHP_EOL;
    echo "hits ganji citys:".$match_count .' ips'.PHP_EOL;

    经测试一个IP匹配1.5毫秒。注意,PHP 的各种socket都是秒级超时,不知道是不是和select的默认最小超时时间是1秒有关系,只有高版本的curl库支持毫秒级超时。

    总的来说 swoole 可以让PHP以很低编程成本起个服务。为这么草根的语言添加了新生活力~

  • 相关阅读:
    【Nginx+Tomcat】高性能负载均衡的Tomcat集群
    【JS-Excel】使用JS导出表格数据、附带解决科学计数法等问题
    【Util】日期工具类总结
    【SpringMVC】url映射传参
    【Linux+Windows】Linux,Windows打包发布到Tomcat并修改映射的ip地址
    【Spring】解决返回json乱码问题
    【API】高德地图API JS实现获取坐标和回显点标记
    ELK-Python(二)
    ELK-Python(一)
    zookeeper集群
  • 原文地址:https://www.cnblogs.com/23lalala/p/3523050.html
Copyright © 2011-2022 走看看