当前位置: 首页 > article >正文

详解根据IP查询所在国家地区的后台实现方案

文章目录

    • 摘要
    • 实现过程
      • 思路
      • GeoLite2 IP库文件引入
        • 准备工作
        • 代码实现
      • Ipdata API
        • 准备工作
          • 官网账号注册
          • 创建API Key
          • API文档:
        • 代码实现
    • 完整代码
    • 总结

摘要

  本文详解如何封装一个根据IP获取所在国家地区的接口。不管是网站、APP还是PC软件,如果你的目标客户包含国内和海外,那必然有多语言等需要区分用户所在区域的功能,虽然说IP定位会有开VPN的特殊场景影响导致不准确(国内被定位到海外,一般来说产品可接受),并且APP本身有其他判断用户所在地的方式,比如安卓可以通过GPS,但是对于一个完整的方案来说,在客户端其他更准确的方式失效或者由于用户不给APP权限导致无法使用时,有一个IP查询的接口对接也是一个必要的兜底方式。

实现过程

思路

  世界范围内IP的数据是不停在更新的,为确保尽可能多的IP能通过接口查询到对应的所在区域,我们需要更新尽量频繁的数据源,ipdata是个不错的选择,它提供API,传入IP即可返回IP相关的国家、城市、时区等信息,但是Ipdata免费调用次数只有一天1500次,需要更多调用次数得花钱。所以我们搭配一个固定的ip数据文件GeoLite2-City.mmdb查询,优先从项目集成的数据文件查询,查询不到才去调用ipdata,并且还可以做个ipdata调用结果的缓存设计,毕竟一个ip属于什么国家是不会频繁变化的,你哪怕缓存几天,都能大大降低ipdata调用次数的浪费。

GeoLite2 IP库文件引入

准备工作

  你可以直接从MaxMind的官方网站上下载GeoLite2-City.mmdb数据库文件:下载链接。
  服务器部署的时候将文件放到服务器上,如果是容器部署服务将文件挂载到容器的自定义目录中,比如我举例叫/home/my_dir/目录。

代码实现

  Maven依赖:

<dependency>
    <groupId>com.maxmind.geoip2</groupId>
    <artifactId>geoip2</artifactId>
    <version>2.8.1</version>
</dependency>

  写一个根据GeoLite2-City.mmdb数据文件查询IP所在国家编码的方法,如果是IP属于中国并且IP在数据文件中,就会返回”CN“:

private String getCountry(String ip) throws IOException, GeoIp2Exception {
   // 容器中读取数据文件的路径
   File database = new File("/home/my_dir/GeoLite2-City.mmdb");
   // 读取数据库内容
   DatabaseReader reader = new DatabaseReader.Builder(database).build();
   InetAddress ipAddress = InetAddress.getByName(ip);
   // 获取查询结果
   CityResponse response = reader.city(ipAddress);
   //获取country
   Country country = response.getCountry();
   return country.getIsoCode();
}

Ipdata API

准备工作

  接下来进入我们的重点,我们来看ipdata的调用方式和实现。

官网账号注册

  官网注册页地址:官网。
在这里插入图片描述

创建API Key

  登录后你能看到官方为每个用户账号分配了一个API key:
在这里插入图片描述

API文档:

  从接口文档可以看出调用方式非常简单,Ip+API key GET请求即可,接口文档地址:文档。
在这里插入图片描述

代码实现

  我们来看下ipdata调用逻辑的代码实现:

private Map<String, String> getCountryByIpData(String ip) {
    String url = "https://api.ipdata.co/" + ip + "?api-key=" + ipDataSecret;
    String resultStr = HttpUtils.get(url, null);
    JSONObject resultObject = JSON.parseObject(resultStr);
    Map<String, String> result = new HashMap<>();
    if (resultObject != null) {
        result.put("countryCode", resultObject.getString("country_code"));
        result.put("countryName", resultObject.getString("country_name"));
        result.put("regionCode", resultObject.getString("region_code"));
        result.put("city", resultObject.getString("city"));
        String timeZoneStr = resultObject.getString("time_zone");
        if (timeZoneStr != null) {
            JSONObject timeZoneObject = JSON.parseObject(timeZoneStr);
            result.put("timeZone", timeZoneObject.getString("name"));
        }
    }
    return result;
}

完整代码

  接下来给读者提供完整的代码实现:

// 优先查询数据库文件,查询不到才使用ipdata
public String getUserCountryCodeByIp(String ip) {
    log.info("Enter service.");
    String code = null;
    try {
        code = getCountry(ip);
        if (StringUtils.isBlank(code)) {
            code = getUserCountryCodeByIpData(ip).getCountryCode();
        }
    } catch (IOException | GeoIp2Exception e) {
        log.error("Get country code error, msg is [{}]", e.getMessage());
        code = getUserCountryCodeByIpData(ip).getCountryCode();
    } catch (Exception ex) {
        ex.printStackTrace();
        code = getUserCountryCodeByIpData(ip).getCountryCode();
    }
    return code;
}

private String getCountry(String ip) throws IOException, GeoIp2Exception {
    // 容器中读取数据文件的路径
    File database = new File("/home/my_dir/GeoLite2-City.mmdb");
    // 读取数据库内容
    DatabaseReader reader = new DatabaseReader.Builder(database).build();
    InetAddress ipAddress = InetAddress.getByName(ip);
    // 获取查询结果
    CityResponse response = reader.city(ipAddress);
    //获取country
    Country country = response.getCountry();
    return country.getIsoCode();
}

public GetCountryCodeVo getUserCountryCodeByIpData(String ip) {
    log.info("Enter service.");
    GetCountryCodeVo result = new GetCountryCodeVo();
    result.setIp(ip);
    result.setCountryCode("");
    try {
        long timestamp = System.currentTimeMillis();
        String redisStr = redisUtil.getString("Data_all:" + ip);
        // 如果缓存里有则不查ipdata了
        if (redisStr != null) {
            log.info("ip:{}, redis IP_ipData_all result:{}.", ip, redisStr);
            result.setCountryCode(redisStr.split(":")[0].equals("-") ? "" : redisStr.split(":")[0]);
            result.setCountryName(redisStr.split(":")[1].equals("-") ? "" : redisStr.split(":")[1]);
            result.setRegionCode(redisStr.split(":")[2].equals("-") ? "" : redisStr.split(":")[2]);
            result.setCity(redisStr.split(":")[3].equals("-") ? "" : redisStr.split(":")[3]);
            result.setTimeZone(redisStr.split(":")[4].equals("-") ? "" : redisStr.split(":")[4]);
        } else {
            Map<String, String> container = getCountryByIpData(ip);
            result.setCountryCode(container.get("countryCode"));
            result.setCountryName(container.get("countryName"));
            result.setRegionCode(container.get("regionCode"));
            result.setCity(container.get("city"));
            result.setTimeZone(container.get("timeZone"));
            String allInfoStr = (StringUtils.isBlank(result.getCountryCode()) ? "-" : result.getCountryCode()) + ":"
                    + (StringUtils.isBlank(result.getCountryName()) ? "-" : result.getCountryName()) + ":"
                    + (StringUtils.isBlank(result.getRegionCode()) ? "-" : result.getRegionCode()) + ":"
                    + (StringUtils.isBlank(result.getCity()) ? "-" : result.getCity()) + ":"
                    + (StringUtils.isBlank(result.getTimeZone()) ? "-" : result.getTimeZone());
            redisUtil.setString("Data_all:" + ip, allInfoStr, 7 * 24 * 60 * 60);
        }


    } catch (Exception ex) {
        ex.printStackTrace();
        return result;
    }
    log.info("Leave service.");
    return result;
}

private Map<String, String> getCountryByIpData(String ip) {
        String url = "https://api.ipdata.co/" + ip + "?api-key=" + ipDataSecret;
        String resultStr = HttpUtils.get(url, null);
        JSONObject resultObject = JSON.parseObject(resultStr);
        Map<String, String> result = new HashMap<>();
        if (resultObject != null) {
            result.put("countryCode", resultObject.getString("country_code"));
            result.put("countryName", resultObject.getString("country_name"));
            result.put("regionCode", resultObject.getString("region_code"));
            result.put("city", resultObject.getString("city"));
            String timeZoneStr = resultObject.getString("time_zone");
            if (timeZoneStr != null) {
                JSONObject timeZoneObject = JSON.parseObject(timeZoneStr);
                result.put("timeZone", timeZoneObject.getString("name"));
            }
        }
        return result;
    }

总结

  这个方案已经是比较完整的实现了,实际项目中按此方案实现后,基本上只需观察ipdata的使用量,我的例子把结果缓存了七天,也就是说同一个IP七天内最多只会触发一次ipdata查询调用,基本上只需要按日活去买合适的调用次数套餐就行了,不会造成浪费,算是给老板省老鼻子钱了,当然,不差钱的话可以不做缓存方案。


http://www.kler.cn/news/335060.html

相关文章:

  • YoloV8改进策略:BackBone改进|CAFormer在YoloV8中的创新应用,显著提升目标检测性能
  • Docker版MKVtoolnix的安装及中文显示
  • 深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)
  • 38 文件包含(标准库头文件、自定义头文件)、相对路径与绝对路径、条件编译(#if、#ifdef、#if define、#ifndef)
  • python-pptx 中 placeholder 和 shape 有什么区别?
  • Python 3 和 MySQL(PyMySQL) 的完美结合
  • 解决数藏平台中因用户使用科技脚本而导致服务器卡顿的方法
  • JavaEE: 数据链路层的奇妙世界
  • 启动redis
  • Node.js安装Express,Node.js支持Typescript以及Express支持Typescript的步骤
  • 代码随想录一刷完结
  • 网站建设开发方法
  • 【论文阅读】Simulating 500 million years of evolution with a language model
  • 实验4 循环结构
  • 测绘地理信息赋能新质生产力
  • Element-UI Plus 暗黑主题切换及自定义主题色
  • 《重生到现代之从零开始的C语言生活》—— 内存函数
  • 数据库(MySQL):使用命令从零开始在Navicat创建一个数据库及其数据表(二).设置主键自增等特点
  • 【笔记】Day1.1.24测试
  • MATLAB plot画线的颜色 形状