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以很低编程成本起个服务。为这么草根的语言添加了新生活力~

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。