1 前言
最近,各大平臺都新增了評論區(qū)顯示發(fā)言者ip歸屬地的功能,例如嗶哩嗶哩,微博,知乎等等,下面,就來講講,Java 中是如何獲取 IP 屬地的
2 獲取IP地址
在Java中有多種獲取IP地址的方式,就不一一介紹了,給出了一個最常用的IP地址獲取方式,僅供參考,代碼如下:
@Slf4j
public class IpUtils {
/**
* 獲取ip地址
* @param request request對象
* @return 返回對應(yīng)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("IPUtils ERROR ",e);
}
return ipAddress;
}
/**
* 獲取mac地址
*/
public static String getMacAddress() throws Exception {
// 取mac地址
byte[] macAddressBytes = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()).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();
}
}
對這里出現(xiàn)的幾個名詞解釋一下:
- X-Forwarded-For:一個 HTTP 擴展頭部,主要是為了讓 Web 服務(wù)器獲取訪問用戶的真實 IP 地址。每個 IP地址,每個值通過逗號+空格分開,最左邊是最原始客戶端的 IP 地址,中間如果有多層代理,每?層代理會將連接它的客戶端 IP 追加在X-Forwarded-For 右邊。
- Proxy-Client-IP:這個一般是經(jīng)過 Apache http 服務(wù)器的請求才會有,用 Apache http做代理時一般會加上Proxy-Client-IP 請求頭
- WL-Proxy-Client-IP:也是通過 Apache http 服務(wù)器,在 weblogic 插件加上的頭。
- X-Real-IP:一般只記錄真實發(fā)出請求的客戶端IP
- HTTP_CLIENT_IP:代理服務(wù)器發(fā)送的HTTP頭。如果是“超級匿名代理”,則返回none值。
3 Ip2region介紹
3.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 次):
- 01,>80%,淘寶IP地址庫,http://ip.taobao.com/%5C
- 02,≈10%,GeoIP,https://geoip.com/%5C
- 03,≈2%,純真 IP 庫,http://www.cz88.net/%5C
備注:如果上述開放 API 或者數(shù)據(jù)都不給開放數(shù)據(jù)時 ip2region 將停止數(shù)據(jù)的更新服務(wù)。
3.2 多查詢客戶端的支持
已經(jīng)集成的客戶端有:java、C#、php、c、python、nodejs、php擴展(php5 和 php7)、golang、rust、lua、lua_c,nginx。
3.3 Ip2region V2.0 特性
3.3.1 標(biāo)準(zhǔn)化的數(shù)據(jù)格式
每個 ip 數(shù)據(jù)段的 region 信息都固定了格式:國家|區(qū)域|省份|城市|ISP,只有中國的數(shù)據(jù)絕大部分精確到了城市,其他國家部分數(shù)據(jù)只能定位到國家,后前的選項全部是 0。
3.3.2 數(shù)據(jù)去重和壓縮
xdb 格式生成程序會自動去重和壓縮部分數(shù)據(jù),默認的全部 IP 數(shù)據(jù),生成的 ip2region.xdb 數(shù)據(jù)庫是 11MiB,隨著數(shù)據(jù)的詳細度增加數(shù)據(jù)庫的大小也慢慢增大。
3.3.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 操作,保持微秒級別的查詢效率。
3.3.4 極速查詢響應(yīng)
v2.0 格式的 xdb 支持億級別的 IP 數(shù)據(jù)段行數(shù),region 信息也可以完全自定義,例如:你可以在 region 中追加特定業(yè)務(wù)需求的數(shù)據(jù),例如:GPS信息/國際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。
4 獲取ip2region.xdb文件
為什么要獲取ip2region.xdb文件呢?是因為獲取ip屬地是以這個文件為基礎(chǔ)的
4.1 直接獲取
文件我已經(jīng)打包到百度網(wǎng)盤了,小伙伴們可以直接下載,地址如下:
鏈接:https://pan.baidu.com/s/1JSB0z2Cwl4umujKUzpBhFA
提取碼:0zz5
4.2 自行打包
下載項目:
鏈接:https://pan.baidu.com/s/1wHjUoHCmyH_PVcB9nf3cLw
提取碼:aq5y
進入到target文件夾,執(zhí)行命令:
java -jar ip2region-maker-1.0.0.jar --src=./ip.merge.txt --dst=./ip2region.xdb
5 使用Ip2region V2.0通過 IP 地址,獲取對應(yīng)的省份、城市
5.1 內(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算法更快。
5.2 具體實現(xiàn)方法
5.2.1 引入ip2region依賴
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.4</version>
</dependency>
5.2.2 ip2region.xdb 文件,放到工程resources目錄下
5.2.3 完全基于ip2region.xdb文件,對用戶ip地址進行轉(zhuǎn)換
/**
* 獲取ip屬地(文件掃描)
* @param ip IP地址
* @return 返回地址
*/
public static String getCityInfoByFile(String ip) {
// 1、創(chuàng)建 searcher 對象
String dbPath = "D:\\gitProject\\workRecord\\src\\main\\resources\\ip\\ip2region.xdb";
Searcher searcher;
try {
searcher = Searcher.newWithFileOnly(dbPath);
} catch (IOException e) {
log.error("failed to create searcher with `{}`: ", dbPath, e);
return null;
}
// 2、查詢
try {
long sTime = System.nanoTime();
String region = searcher.search(ip);
long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
log.info("{region: {}, ioCount: {}, took: {} μs}", region, searcher.getIOCount(), cost);
return region;
} catch (Exception e) {
log.info("failed to search({}): ", ip, e);
}
return null;
// 3、備注:并發(fā)使用,每個線程需要創(chuàng)建一個獨立的 searcher 對象單獨使用。
}
public static void main(String[] args) throws Exception {
getCityInfoByFile("111.0.72.130");
}
輸出:
5.2.4 緩存 VectorIndex 索引,對用戶ip地址進行轉(zhuǎn)換
我們可以提前從 xdb 文件中加載出來 VectorIndex 數(shù)據(jù),然后全局緩存,每次創(chuàng)建Searcher 對象的時候使用全局的 VectorIndex 緩存可以減少一次固定的 IO 操作,從而加速查詢,減少 IO 壓力。
/**
* 獲取ip屬地(緩存 VectorIndex 索引)
* @param ip IP地址
* @return 返回地址
*/
public static String getCityInfoByVectorIndex(String ip) {
String dbPath = "D:\\gitProject\\workRecord\\src\\main\\resources\\ip\\ip2region.xdb";
// 1、從 dbPath 中預(yù)先加載 VectorIndex 緩存,并且把這個得到的數(shù)據(jù)作為全局變量,后續(xù)反復(fù)使用。
byte[] vIndex;
try {
vIndex = Searcher.loadVectorIndexFromFile(dbPath);
} catch (Exception e) {
log.error("failed to load vector index from `{}`: ", dbPath, e);
return null;
}
// 2、使用全局的 vIndex 創(chuàng)建帶 VectorIndex 緩存的查詢對象。
Searcher searcher;
try {
searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
} catch (Exception e) {
log.error("failed to create vectorIndex cached searcher with `{}`: ", dbPath, e);
return null;
}
// 3、查詢
try {
long sTime = System.nanoTime();
String region = searcher.search(ip);
long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
log.info("{region: {}, ioCount: {}, took: {} μs}\n", region, searcher.getIOCount(), cost);
return region;
} catch (Exception e) {
log.error("failed to search({}): ", ip, e);
}
return null;
// 備注:每個線程需要單獨創(chuàng)建一個獨立的 Searcher 對象,但是都共享全局的制度 vIndex 緩存。
}
public static void main(String[] args) throws Exception {
getCityInfoByVectorIndex("111.0.72.130");
}
輸出:
5.2.5 緩存整個 xdb 數(shù)據(jù),對用戶ip地址進行轉(zhuǎn)換
我們也可以預(yù)先加載整個 ip2region.xdb 的數(shù)據(jù)到內(nèi)存,然后基于這個數(shù)據(jù)創(chuàng)建查詢對象來實現(xiàn)完全基于文件的查詢,類似之前的 memory search。
/**
* 獲取ip屬地(緩存整個 xdb 數(shù)據(jù))
* @param ip IP地址
* @return 返回地址
*/
public static String getCityInfoByMemorySearch(String ip) {
String dbPath = "D:\\gitProject\\workRecord\\src\\main\\resources\\ip\\ip2region.xdb";
// 1、從 dbPath 加載整個 xdb 到內(nèi)存。
byte[] cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
log.error("failed to load content from `{}`: ", dbPath, e);
return null;
}
// 2、使用上述的 cBuff 創(chuàng)建一個完全基于內(nèi)存的查詢對象。
Searcher searcher;
try {
searcher = Searcher.newWithBuffer(cBuff);
} catch (Exception e) {
log.error("failed to create content cached searcher: ", e);
return null;
}
// 3、查詢
try {
long sTime = System.nanoTime();
String region = searcher.search(ip);
long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
log.info("{region: {}, ioCount: {}, took: {} μs}\n", region, searcher.getIOCount(), cost);
} catch (Exception e) {
log.error("failed to search({}): ", ip, e);
}
return null;
// 備注:并發(fā)使用,用整個 xdb 數(shù)據(jù)緩存創(chuàng)建的查詢對象可以安全的用于并發(fā),也就是你可以把這個 searcher 對象做成全局對象去跨線程訪問。
}
public static void main(String[] args) throws Exception {
getCityInfoByMemorySearch("111.0.72.130");
}
輸出:文章來源:http://www.zghlxwxcb.cn/news/detail-423526.html
6 總結(jié)
根據(jù)IP地址獲取對應(yīng)歸屬地的介紹就到這了,有問題的小伙伴們可以在評論區(qū)討論文章來源地址http://www.zghlxwxcb.cn/news/detail-423526.html
到了這里,關(guān)于Java根據(jù)IP地址獲取對應(yīng)歸屬地的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!