麻辣GIS微信平台

更多 GIS 干货

微信关注不错过

「GIS教程」再谈使用IP地址定位用户位置(ip2region)

写在前面:本文特别鸣谢 重生之GIS搬砖小分队2号 微信群里的大神J哥。 此大哥人帅心美,帮我解决了大难题,感谢!!

在之前的文章《「GIS技巧」使用IP地址定位用户位置的方法小结》中介绍了使用IP地址定位用户位置的方法,但随着时间的推移,好多之前推荐的三方接口都失效了,比如大家都喜欢使用的搜狐接口

https://pv.sohu.com/cityjson?ie=utf-8

目前在我这里已经是一个无法使用的状态,无论哪里访问都是未知,这导致我之前的应用直接出了问题,被测试提出之后开始寻找新的解决方案。经过调研,我最后使用了一个Github开源工具库:ip2region,感觉还可以这里推荐一下。

先说说其他解决方案:

百度、高德

在搜狐、淘宝等一众类似 JSONP 的解决方案失效之后,我首先想到的就是这两个方案。这两个方案哪里都好,除了要花钱

我们领导又比较务实(扣),要能不能找一个不依赖第三方的解决方案?

如图:百度高德的价格列表。

PS:上述价格仅供参考,实际价格会变动。

ip2region

既然不让使用三方服务,那就自己部署一个服务呗,说白了这个就是一个对应表,比如:

113.56.180.66 -- 中国湖北武汉 联通

这里的关键是就是一个IP数据库,比较常见的有国外的GeoIP,国内比较知名的有IPIP,小编之前在文章《「GIS工具」基于地图可视化你的网络请求》中推荐的工具就是出自 IPIP 之手,其定位的准确度甚至能到区县级。不过 也要花钱。就在我要放弃的时候,发现了一个好东西:

https://github.com/lionsoul2014/ip2region

ip2region - 是一个离线IP地址定位库和IP定位数据管理框架,10微秒级别的查询效率,提供了众多主流编程语言的 xdb 数据生成和查询客户端实现。

其宣称的是“准确率99.9%的ip地址定位库”,于是研究了一下,发现有点小骗人,其官方说明的是:

ip2region 旨在于 研究 IP 数据的存储和快速查询的设计和实现,并没有原始 IP 数据的支撑,也不会有商用版本。 本项目的自带的 ./data/ip.merge.txt 原始数据已经很久没有更新,也不会再更新,对于数据精度和更新频率要求很高的使用场景建议购买第三方商用数据

不过小编测试了一下,发现其数据更新是去年的,不过给老大看了看,说够用了,于是我就使用这了这个版本。

ip2region接入

既然 ip2region 查询效率高,也有默认数据,也可以接商业数据,那么思路就有了:

  1. 如果精度高,买IPIP的数据,否则用默认数据
  2. 选一个系统需要的语言接入

目前 ip2region 支持的接入方式如下:

  • golang
  • php
  • java
  • lua
  • C
  • luaC
  • rust
  • python
  • nodejs
  • csharp
  • php ext
  • nginx

我负责的这个系统比较老,使用的是PHP,其他接入方式可以根据官网方法来。使用PHP开始使用的是文件读写的方式,但这种方式的IO压力会比较大,并发上来之后可能会有风险。最后请教了重生之GIS搬砖小分队2号 微信群里的大神J哥,开始尝试了memcached,发现单个数据最大只能1M,而这个数据有10M多,在J哥的建议下,使用的Redis,大约代码如下:

获取访问用户的IP地址;

function getIP() {
  $ip = isset($_SERVER['HTTP_CLIENT_IP']) 
    ? $_SERVER['HTTP_CLIENT_IP'] 
    : (isset($_SERVER['HTTP_X_FORWARDED_FOR']) 
      ? $_SERVER['HTTP_X_FORWARDED_FOR'] 
      : $_SERVER['REMOTE_ADDR']);
  return $ip;
}

将IP地址转换为地理位置:

function ip2region($ip) {
  $key = 'ip_data';
  $countKey = 'count_key';
  $redis = new Redis();
  $redis->connect("127.0.0.1", 6379);
  $cacheBuff = $redis->get($key);

  if ($cacheBuff) {
    return 'cache:' .ipSearch($cacheBuff, $ip);
  } else {
    $dbPath = __DIR__.'/ip2region.xdb';
    $cBuff = XdbSearcher::loadContentFromFile($dbPath);
    if ($cBuff === null) {
        // printf("failed to load content buffer from '%s'\n", $dbPath);
        return 'error';
    }
    $redis->set($key, $cBuff);
    return ipSearch($cBuff, $ip);
  }
}

function ipSearch($buff, $ip) {
  try {
      $searcher = XdbSearcher::newWithBuffer($buff);
  } catch (Exception $e) {
      return 'error';
  }

  $region = $searcher->search($ip);
  if ($region === null) {
      return 'unknown';
  }
  return $region;
}

完事儿交差,不过有个小发现,启用了redis之后,由于这玩意是个单线程,CPU的占用会不定时的增加,后面看看有没有其他方法再优化下。

参考

  1. ip2region: https://github.com/lionsoul2014/ip2region/
  2. ipip官网: https://www.ipip.net/
  3. GeoIP: https://www.maxmind.com/en/geoip-demo

相关阅读

麻辣GIS-Sailor

作者:

GIS爱好者,学GIS,更爱玩GIS。

声明

1.本文所分享的所有需要用户下载使用的内容(包括但不限于软件、数据、图片)来自于网络或者麻辣GIS粉丝自行分享,版权归该下载资源的合法拥有者所有,如有侵权请第一时间联系本站删除。

2.下载内容仅限个人学习使用,请切勿用作商用等其他用途,否则后果自负。

手机阅读
公众号关注
知识星球
手机阅读
麻辣GIS微信公众号关注
最新GIS干货
关注麻辣GIS知识星球
私享圈子

留言板(小编看到第一时间回复)