欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java獲取IP地址及對應(yīng)的歸屬地的方法詳解

 更新時間:2025年03月04日 09:51:35   作者:Javaの甘乃迪  
這篇文章主要為大家詳細(xì)介紹了如何利用Java獲取IP地址及對應(yīng)的歸屬地,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

細(xì)心的朋友們可能已經(jīng)發(fā)現(xiàn)了,先在抖音、知乎、快手、小紅書等這些平臺已經(jīng)上線了“網(wǎng)絡(luò)用戶顯示 IP 的功能”,境外用戶顯示的是國家,國內(nèi)的用戶顯示的省份,而且此項顯示無法關(guān)閉,歸屬地強制顯示。

作為一個努力搬磚的碼農(nóng),我們肯定要來看一下這個功能是如何實現(xiàn)的,今天這篇文章,就來講述一下這個功能是怎么實現(xiàn)的。

一、獲取訪問的IP地址

HttpServletRequest 獲取 IP

首先我們來看一下,在 Java 中,是如何獲取到 IP 屬地的,主要有以下兩步:

通過 HttpServletRequest 對象,獲取用戶的 【IP】 地址

通過 IP 地址,獲取對應(yīng)的【省份、城市】

我這里寫一個工具類用于獲取 IP 地址,因為用戶的每次 Request 請求都會攜帶請求的 IP 地址放到請求頭中,所以我們可以通過截取請求中的 IP 來獲取 IP 地址,代碼如下:

package com.test.java.util;
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
 
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Objects;
 
/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {
 
    /**
     * 獲取請求的 IP 地址
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if ("127.0.0.1".equals(ip)) {
                // 根據(jù)網(wǎng)卡取本機配置的 IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("獲取IP地址異常,{}", e.getMessage());
                }
                if (inet != null) {
                    ip = inet.getHostAddress();
                }
            }
        }
        // 多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        // 本機訪問
        if ("localhost".equalsIgnoreCase(ip) || "127.0.0.1".equalsIgnoreCase(ip) || "0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)) {
            // 根據(jù)網(wǎng)卡取本機配置的IP
            InetAddress inet;
            try {
                inet = InetAddress.getLocalHost();
                ip = inet.getHostAddress();
            } catch (Exception e) {
                e.printStackTrace();
                log.error("獲取本機IP地址異常,{}", e.getMessage());
            }
        }
        // 如果查找不到 IP,可以返回 127.0.0.1,可以做一定的處理,但是這里不考慮
        // if (ip == null) {
        //     return "127.0.0.1";
        // }
        return ip;
    }
 
    /**
     * 獲取IP地址
     */
    public static String getIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ipAddress = headers.getFirst("X-Forwarded-For");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();
            if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) {
                // 根據(jù)網(wǎng)卡取本機配置的IP
                try {
                    InetAddress inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                } catch (Exception e) {
                    log.error("獲取IP地址異常,{}", e.getMessage());
                }
            }
        }
 
        // 對于通過多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割
        if (ipAddress != null && ipAddress.indexOf(",") > 0) {
            ipAddress = ipAddress.split(",")[0];
        }
        return ipAddress;
    }
 
    /**
     * 獲取mac地址
     */
    public static String getMacIpAddress() {
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            byte[] macAddressBytes = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
            // 將mac地址拼裝成String
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < macAddressBytes.length; i++) {
                if (i != 0) {
                    sb.append("-");
                }
                // mac[i] & 0xFF 是為了把byte轉(zhuǎn)化為正整數(shù)
                String s = Integer.toHexString(macAddressBytes[i] & 0xFF);
                sb.append(s.length() == 1 ? 0 + s : s);
            }
            return sb.toString().trim().toUpperCase();
        } catch (Exception e) {
            log.error("Mac獲取IP地址異常,{}", e.getMessage());
        }
        return "";
    }
 
}

這里出現(xiàn)了三個名詞:

  • X-Forwarded-For:一個 HTTP 擴展頭部,主要是為了讓 Web 服務(wù)器獲取訪問用戶的真實 IP 地址。每個 IP 地址,每個值通過逗號+空格分開,最左邊是最原始客戶端的 IP 地址,中間如果有多層代理,每?層代理會將連接它的客戶端 IP 追加在 X-Forwarded-For 右邊
  • X-Real-IP:一般只記錄真實發(fā)出請求的客戶端IP
  • Proxy-Client-IP:這個一般是經(jīng)過 Apache http 服務(wù)器的請求才會有,用 Apache http 做代理時一般會加上 Proxy-Client-IP 請求頭
  • WL-Proxy-Client-IP:也是通過 Apache http 服務(wù)器,在 weblogic 插件加上的頭

二、通過IP地址獲取對應(yīng)的歸屬地

通過第三方地址庫 Ip2region,獲取IP歸屬地。

2.1 Ip2region

Ip2region 是一個 Gthub 的開源項目,即 Ip2region 開源項目。

github地址:https://github.com/lionsoul2014/ip2region

這個開源庫目前已經(jīng)更新到了 V2 的版本,現(xiàn)在的它是一個強大的離線IP地址定位庫和IP定位數(shù)據(jù)管理框架,其達到了微秒級別的查詢效率,還提供了眾多主流編程語言的 xdb 數(shù)據(jù)生成和查詢客戶端實現(xiàn),可以說是非常得好用

2.1.1 高達 99.9 % 的查詢準(zhǔn)確率

數(shù)據(jù)聚合了一些知名 ip 到地名查詢提供商的數(shù)據(jù),這些是他們官方的準(zhǔn)確率,經(jīng)測試著實比經(jīng)典的純真 IP 定位準(zhǔn)確一些。

ip2region 的數(shù)據(jù)聚合自以下服務(wù)商的開放 API 或者數(shù)據(jù)(升級程序每秒請求次數(shù) 2 到 4 次),比例如下:

80%, 淘寶 IP 地址庫, ip.taobao.com/
≈10%, GeoIP, geoip.com/
≈2%, 純真 IP 庫, www.cz88.net/

2.1.2 Ip2region V2.0 特性

1.IP 數(shù)據(jù)管理框架

xdb 支持億級別的 IP 數(shù)據(jù)段行數(shù),默認(rèn)的 region 信息都固定了格式:國家|區(qū)域|省份|城市|ISP,缺省的地域信息默認(rèn)是0。

只有中國的數(shù)據(jù)精確到了城市,其他國家有部分?jǐn)?shù)據(jù)只能定位到國家,后前的選項全部是 0,已經(jīng)包含了全部你能查到的大大小小的國家

生成的數(shù)據(jù)庫文件 ip2region.db 只有幾 MB,最小的版本只有 1.5MB,隨著數(shù)據(jù)的詳細(xì)度增加數(shù)據(jù)庫的大小也慢慢增大,目前還沒超過 8MB。

region 信息支持完全自定義,例如:你可以在 region 中追加特定業(yè)務(wù)需求的數(shù)據(jù),例如:GPS信息/國際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。

2.數(shù)據(jù)去重和壓縮

xdb 格式生成程序會自動去重和壓縮部分?jǐn)?shù)據(jù),默認(rèn)的全部 IP 數(shù)據(jù),生成的 ip2region.xdb 數(shù)據(jù)庫是 11MiB,隨著數(shù)據(jù)的詳細(xì)度增加數(shù)據(jù)庫的大小也慢慢增大。

3.極速查詢響應(yīng)

即使是完全基于 xdb 文件的查詢,單次查詢響應(yīng)時間在十微秒級別,可通過如下兩種方式開啟內(nèi)存加速查詢:

  • vIndex 索引緩存:使用固定的 512KiB 的內(nèi)存空間緩存 vector index 數(shù)據(jù),減少一次 IO 磁盤操作,保持平均查詢效率穩(wěn)定在10-20微秒之間
  • xdb 整個文件緩存:將整個 xdb 文件全部加載到內(nèi)存,內(nèi)存占用等同于 xdb 文件大小,無磁盤 IO 操作,保持微秒級別的查詢效率。

4.內(nèi)置的三種查詢算法

全部的查詢客戶端單次查詢都在 0.x 毫秒級別,內(nèi)置了三種查詢算法:

  • memory 算法:整個數(shù)據(jù)庫全部載入內(nèi)存,單次查詢都在0.1x毫秒內(nèi),C語言的客戶端單次查詢在0.00x毫秒級別。
  • binary 算法:基于二分查找,基于ip2region.db文件,不需要載入內(nèi)存,單次查詢在0.x毫秒級別。
  • b-tree 算法:基于btree算法,基于ip2region.db文件,不需要載入內(nèi)存,單詞查詢在0.x毫秒級別,比binary算法更快。

2.1.3 多語言以及查詢客戶端的支持

已經(jīng)有的客戶端:Java、C#、php、C、Python、Node.js、PHP 拓展(PHP 5 和 PHP 7)等,主要如下:

2.2 Ip2region xdb Java 查詢客戶端實現(xiàn)

這里簡單展示一下 Java 的實現(xiàn),這里使用開發(fā)中常用的 Maven 實現(xiàn)的方式:

2.2.1 引入 Maven 倉庫

<!-- IP地址轉(zhuǎn)歸屬地 -->
<dependency>
   <groupId>org.lionsoul</groupId>
   <artifactId>ip2region</artifactId>
   <version>2.6.4</version>
</dependency>

2.2.2 ip2region.xdb 文件,放到工程resources目錄下

2.2.3 實現(xiàn)方式

基于文件查詢

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;
 
import java.util.concurrent.TimeUnit;
 
/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {
 
    // ip2region.xdb 文件地址常量(本地xdb文件路徑)
    public static String XDB_PATH = "D:\\IDEA2022.2.3\\workspace\\java\\src\\main\\resources\\ip\\ip2region.xdb";
    
    /**
     * 完全基于ip2region.xdb文件,對用戶ip地址進行轉(zhuǎn)換
     * 注:并發(fā)調(diào)用時,每個線程需創(chuàng)建一個獨立的searcher對象 單獨使用。
     */
    public static String getIpPossessionByFile(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、創(chuàng)建 searcher 對象
                Searcher searcher = Searcher.newWithFileOnly(XDB_PATH);
                // 2、查詢
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                log.info("{地區(qū): {}, IO操作數(shù): {}, 耗時: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("獲取IP地址異常:{} ", e.getMessage());
                throw new RuntimeException("獲取IP地址異常");
            }
        }
        return "未知";
    }
 
}

緩存VectorIndex索引

我們可以提前從 xdb 文件中加載出來 VectorIndex 數(shù)據(jù),然后全局緩存,每次創(chuàng)建 Searcher 對象的時候使用全局的 VectorIndex 緩存可以減少一次固定的 IO 操作,從而加速查詢,減少 IO 壓力。

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;
 
import java.util.concurrent.TimeUnit;
 
/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {
 
    // ip2region.xdb 文件地址常量(本地xdb文件路徑)
    public static String XDB_PATH = "D:\\IDEA2022.2.3\\workspace\\java\\src\\main\\resources\\ip\\ip2region.xdb";
 
    /**
     * 緩存 VectorIndex 索引,對用戶ip地址進行轉(zhuǎn)換
     * 注:每個線程需要單獨創(chuàng)建一個獨立的 Searcher 對象,但是都共享全局變量 vIndex 緩存。
     */
    public static String getCityInfoByVectorIndex(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、從 XDB_PATH 中預(yù)先加載 VectorIndex 緩存,并且作為全局變量,后續(xù)反復(fù)使用。
                byte[] vIndex = Searcher.loadVectorIndexFromFile(XDB_PATH);
                // 2、使用全局的 vIndex 創(chuàng)建帶 VectorIndex 緩存的查詢對象。
                Searcher searcher = Searcher.newWithVectorIndex(XDB_PATH, vIndex);
                // 3、查詢
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                log.info("{地區(qū): {}, IO操作數(shù): {}, 耗時: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("獲取IP地址異常:{} ", e.getMessage());
                throw new RuntimeException("獲取IP地址異常");
            }
        }
        return "未知";
    }
 
}

緩存整個 xdb 數(shù)據(jù)

我們也可以預(yù)先加載整個 ip2region.xdb 的數(shù)據(jù)到內(nèi)存,然后基于這個數(shù)據(jù)創(chuàng)建查詢對象來實現(xiàn)完全基于文件的查詢,類似之前的 memory search。

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;
 
import java.util.concurrent.TimeUnit;
 
/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {
 
    // ip2region.xdb 文件地址常量
    public static String XDB_PATH = "D:\\java\\src\\main\\resources\\ip\\ip2region.xdb";
 
    /**
     * 緩存整個 xdb 數(shù)據(jù),對用戶ip地址進行轉(zhuǎn)換
     * 注:并發(fā)使用時,用整個 xdb 數(shù)據(jù)緩存創(chuàng)建的查詢對象可以安全的用于并發(fā),也就是你可以把這個 searcher 對象做成全局對象去跨線程訪問。
     */
    public static String getCityInfoByMemorySearch(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、從 XDB_PATH 加載整個 xdb 到內(nèi)存。
                byte[] cBuff = Searcher.loadContentFromFile(XDB_PATH);
                // 2、使用上述的 cBuff 創(chuàng)建一個完全基于內(nèi)存的查詢對象。
                Searcher searcher = Searcher.newWithBuffer(cBuff);
                // 3、查詢
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                log.info("{地區(qū): {}, IO操作數(shù): {}, 耗時: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("獲取IP地址異常:{} ", e.getMessage());
                throw new RuntimeException("獲取IP地址異常");
            }
        }
        return "未知";
    }
 
}

通過第三方API查詢(在線查詢)

前面介紹的3種方法都是離線查詢,該方法主要通過第三方提供的官網(wǎng)或API接口去實現(xiàn)在線查詢的功能,但有個弊端就是特別依賴對方的服務(wù)器,一旦對方的服務(wù)器宕機就無法訪問了。具體實現(xiàn)效果跟之前介紹的離線查詢方法是一樣的。

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
 
/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {
 
    /**
     * 在線查詢IP歸屬地
     */
    public static String getIpAddressByOnline(String ip) {
        try {
            //1、創(chuàng)建 URLConnction
            URL url = new URL("http://ip-api.com/json/" + ip + "?lang=zh-CN");
 
            //2、設(shè)置connection的屬性
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(20000);
            connection.setReadTimeout(20000);
            connection.setRequestProperty("content-type", "application/json; charset=utf-8");
 
            //3.連接
            connection.connect();
 
            //4.獲取內(nèi)容
            InputStream inputStream = connection.getInputStream();
 
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            //System.out.println(sb);
 
            String str = sb.toString();
            if (StringUtils.isNotEmpty(str)) {
                // string轉(zhuǎn)map
                Gson gson = new Gson();
                Map<String, Object> map = new HashMap<>();
                map = gson.fromJson(str, map.getClass());
                String country = (String) map.get("country");
                String city = (String) map.get("city");
                String regionName = (String) map.get("regionName");
                System.out.println("國家:" + country);
                System.out.println("城市:" + city);
                System.out.println("地區(qū):" + regionName);
                return country + "|" + city + "|" + regionName;
            }
        } catch (Exception e) {
            log.error("在線查詢IP地址異常,{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
        return null;
    }
 
}

最優(yōu)方案

其實我推薦可以將方法結(jié)合使用。先采用離線查詢,如果發(fā)現(xiàn)地址為null的話,則調(diào)用在線查詢方法。這樣在一定的程度上能夠保證數(shù)據(jù)的完整性。完整的工具類如下:

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;
 
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
 
/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {
 
    // ip2region.xdb 文件地址常量(本地xdb文件路徑)
    public static String XDB_PATH = "D:\\IDEA2022.2.3\\workspace\\java\\src\\main\\resources\\ip\\ip2region.xdb";
 
    /**
     * 獲取IP地址:
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("X-Forwarded-For");
            if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
                // 多次反向代理后會有多個ip值,第一個ip才是真實ip
                if (ipAddress.contains(",")) {
                    ipAddress = ipAddress.split(",")[0];
                }
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("獲取IP地址異常,{}", e.getMessage());
        }
        return ipAddress;
    }
 
    /**
     * 獲取mac地址
     */
    public static String getMacIpAddress() {
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            byte[] macAddressBytes = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
            // 將mac地址拼裝成String
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < macAddressBytes.length; i++) {
                if (i != 0) {
                    sb.append("-");
                }
                // mac[i] & 0xFF 是為了把byte轉(zhuǎn)化為正整數(shù)
                String s = Integer.toHexString(macAddressBytes[i] & 0xFF);
                sb.append(s.length() == 1 ? 0 + s : s);
            }
            return sb.toString().trim().toUpperCase();
        } catch (Exception e) {
            log.error("Mac獲取IP地址異常,{}", e.getMessage());
        }
        return "";
    }
 
    /**
     * 方法一:完全基于ip2region.xdb文件,對用戶ip地址進行轉(zhuǎn)換
     * 注:并發(fā)調(diào)用時,每個線程需創(chuàng)建一個獨立的searcher對象 單獨使用。
     */
    public static String getIpPossessionByFile(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、創(chuàng)建 searcher 對象
                Searcher searcher = Searcher.newWithFileOnly(XDB_PATH);
                // 2、查詢
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                //log.info("{地區(qū): {}, IO操作數(shù): {}, 耗時: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("獲取IP地址異常:{} ", e.getMessage());
                throw new RuntimeException("獲取IP地址異常");
            }
        }
        return "未知";
    }
 
    /**
     * 方法二:緩存 VectorIndex 索引,對用戶ip地址進行轉(zhuǎn)換
     * 注:每個線程需要單獨創(chuàng)建一個獨立的 Searcher 對象,但是都共享全局變量 vIndex 緩存。
     */
    public static String getCityInfoByVectorIndex(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、從 XDB_PATH 中預(yù)先加載 VectorIndex 緩存,并且作為全局變量,后續(xù)反復(fù)使用。
                byte[] vIndex = Searcher.loadVectorIndexFromFile(XDB_PATH);
                // 2、使用全局的 vIndex 創(chuàng)建帶 VectorIndex 緩存的查詢對象。
                Searcher searcher = Searcher.newWithVectorIndex(XDB_PATH, vIndex);
                // 3、查詢
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                //log.info("{地區(qū): {}, IO操作數(shù): {}, 耗時: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("獲取IP地址異常:{} ", e.getMessage());
                throw new RuntimeException("獲取IP地址異常");
            }
        }
        return "未知";
    }
 
    /**
     * 方法三:緩存整個 xdb 數(shù)據(jù),對用戶ip地址進行轉(zhuǎn)換
     * 注:并發(fā)使用時,用整個 xdb 數(shù)據(jù)緩存創(chuàng)建的查詢對象可以安全的用于并發(fā),也就是你可以把這個 searcher 對象做成全局對象去跨線程訪問。
     */
    public static String getCityInfoByMemorySearch(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、從 XDB_PATH 加載整個 xdb 到內(nèi)存。
                byte[] cBuff = Searcher.loadContentFromFile(XDB_PATH);
                // 2、使用上述的 cBuff 創(chuàng)建一個完全基于內(nèi)存的查詢對象。
                Searcher searcher = Searcher.newWithBuffer(cBuff);
                // 3、查詢
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                //log.info("{地區(qū): {}, IO操作數(shù): {}, 耗時: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("獲取IP地址異常:{} ", e.getMessage());
                throw new RuntimeException("獲取IP地址異常");
            }
        }
        return "未知";
    }
 
    /**
     * 方法四:在線獲取IP地址
     * 注:通過別人或者官網(wǎng)提供的API接口去實現(xiàn)查詢的功能,弊端就是特別依賴別人的服務(wù)器,一旦服務(wù)器宕機就無法訪問了。
     */
    public static String getIpAddressByOnline(String ip) {
 
        try {
            //1、創(chuàng)建 URLConnction
            URL url = new URL("http://ip-api.com/json/" + ip + "?lang=zh-CN");
 
            //2、設(shè)置connection的屬性
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(20000);
            connection.setReadTimeout(20000);
            connection.setRequestProperty("content-type", "application/json; charset=utf-8");
 
            //3.連接
            connection.connect();
 
            //4.獲取內(nèi)容
            InputStream inputStream = connection.getInputStream();
 
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            //System.out.println(sb);
 
            String str = sb.toString();
            if (StringUtils.isNotEmpty(str)) {
                // string轉(zhuǎn)map
                Gson gson = new Gson();
                Map<String, Object> map = new HashMap<>();
                map = gson.fromJson(str, map.getClass());
                String country = (String) map.get("country");
                String city = (String) map.get("city");
                String regionName = (String) map.get("regionName");
                //log.info("【國家】{},【城市】{},【地區(qū)】{}", country, city, regionName);
                return country + "|" + city + "|" + regionName;
            }
        } catch (Exception e) {
            log.error("在線查詢IP地址異常,{}", e.getMessage());
            throw new RuntimeException("在線查詢IP地址異常");
        }
        return null;
    }
 
    /**
     * 根據(jù)IP地址 獲取歸屬地
     */
    public static String getIpPossession(String ipAddress) {
        if (StringUtils.isNotEmpty(ipAddress)) {
            ipAddress = ipAddress.replace("|", " ");
            String[] cityList = ipAddress.split(" ");
            if (cityList.length > 0) {
                // 國內(nèi)的顯示到具體的省
                if ("中國".equals(cityList[0])) {
                    if (cityList.length > 1) {
                        return cityList[1];
                    }
                }
                // 國外顯示到國家
                return cityList[0];
            }
        }
        return "未知";
    }
 
    public static void main(String[] args) {
 
        String ip = "183.162.252.0";// 國內(nèi)IP
        String abroadIp = "48.119.248.100"; // 國外IP
 
        System.out.println("方法一(國內(nèi)):" + getIpPossessionByFile(ip));
        System.out.println("方法二(國內(nèi)):" + getCityInfoByVectorIndex(ip));
        System.out.println("方法三(國內(nèi)):" + getCityInfoByMemorySearch(ip));
        System.out.println("方法四(國內(nèi)):" + getIpAddressByOnline(ip));
 
        System.out.println("方法一(國外):" + getIpPossessionByFile(abroadIp));
        System.out.println("方法二(國外):" + getCityInfoByVectorIndex(abroadIp));
        System.out.println("方法三(國外):" + getCityInfoByMemorySearch(abroadIp));
        System.out.println("方法四(國外):" + getIpAddressByOnline(abroadIp));
 
        //System.out.println("歸屬地(國內(nèi)):" + getIpPossession(getCityInfoByVectorIndex(ip)));
        //System.out.println("歸屬地(國外):" + getIpPossession(getCityInfoByVectorIndex(abroadIp)));
 
    }
}

以上就是Java獲取IP地址及對應(yīng)的歸屬地的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Java獲取IP地址和歸屬地的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 后端返回各種圖片形式在前端的轉(zhuǎn)換及展示方法對比

    后端返回各種圖片形式在前端的轉(zhuǎn)換及展示方法對比

    這篇文章主要給大家介紹了關(guān)于后端返回各種圖片形式在前端的轉(zhuǎn)換及展示方法對比的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-06-06
  • SpringMVC中ModelAndView用法小結(jié)

    SpringMVC中ModelAndView用法小結(jié)

    本文主要介紹了SpringMVC中ModelAndView用法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • 淺談Timer和TimerTask與線程的關(guān)系

    淺談Timer和TimerTask與線程的關(guān)系

    下面小編就為大家?guī)硪黄獪\談Timer和TimerTask與線程的關(guān)系。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • JAVA  字符串加密、密碼加密實現(xiàn)方法

    JAVA 字符串加密、密碼加密實現(xiàn)方法

    這篇文章主要介紹了JAVA 字符串加密、密碼加密實現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • SpringBoot Entity中枚舉類型詳細(xì)使用介紹

    SpringBoot Entity中枚舉類型詳細(xì)使用介紹

    本文介紹SpringBoot如何在Entity(DAO)中使用枚舉類型。(本文使用MyBatis-Plus)。在實際開發(fā)中,經(jīng)常會遇到表示類型或者狀態(tài)的情況,比如:有三種支付方式:微信、支付寶、銀聯(lián)。本文介紹如何這種場景的方案對比,并用實例來介紹如何用枚舉這種最優(yōu)雅的來表示
    2022-10-10
  • gateway與spring-boot-starter-web沖突問題的解決

    gateway與spring-boot-starter-web沖突問題的解決

    這篇文章主要介紹了gateway與spring-boot-starter-web沖突問題的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java代碼讀取文件緩存問題解決

    Java代碼讀取文件緩存問題解決

    最近遇到了一個Java文件讀取的緩存問題,打遠程斷點出現(xiàn)的也是原來的老代碼參數(shù),本文就介紹一下解決方法,感興趣的可以了解一下
    2021-05-05
  • SpringBoot如何實現(xiàn)緩存預(yù)熱

    SpringBoot如何實現(xiàn)緩存預(yù)熱

    緩存預(yù)熱是指在 Spring Boot 項目啟動時,預(yù)先將數(shù)據(jù)加載到緩存系統(tǒng)(如 Redis)中的一種機制,本文主要介紹了SpringBoot如何實現(xiàn)緩存預(yù)熱,感興趣的可以了解下
    2024-12-12
  • Spring Security的簡單使用

    Spring Security的簡單使用

    這篇文章主要介紹了Spring Security的簡單使用,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot,感興趣的朋友可以了解下
    2021-04-04
  • Java啟動命令大全(匯總)

    Java啟動命令大全(匯總)

    Java啟動命令是所有java應(yīng)用程序的入口,通過它來啟動Java運行時環(huán)境,并加載相關(guān)的class,本文希望做一個Java啟動命令的匯總,和各位同道分享,也便于日后作為自己的參考
    2023-09-09

最新評論