Springboot整合ip2region實現(xiàn)用戶ip歸屬地獲取
1、 Ip2region 是什么
ip2region - 是一個離線IP地址定位庫和IP定位數(shù)據(jù)管理框架,10微秒級別的查詢效率,提供了眾多主流編程語言的 xdb 數(shù)據(jù)生成和查詢客戶端實現(xiàn)。
2、Ip2region 特性
2.1、IP 數(shù)據(jù)管理框架
xdb 支持億級別的 IP 數(shù)據(jù)段行數(shù),默認的 region 信息都固定了格式:國家|區(qū)域|省份|城市|ISP,缺省的地域信息默認是0。 region 信息支持完全自定義,例如:你
可以在 region 中追加特定業(yè)務需求的數(shù)據(jù),例如:GPS信息/國際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。
2.2、數(shù)據(jù)去重和壓縮
xdb 格式生成程序會自動去重和壓縮部分數(shù)據(jù),默認的全部 IP 數(shù)據(jù),生成的 ip2region.xdb 數(shù)據(jù)庫是 11MiB,隨著數(shù)據(jù)的詳細度增加數(shù)據(jù)庫的大小也慢慢增大。
2.3、極速查詢響應
即使是完全基于 xdb 文件的查詢,單次查詢響應時間在十微秒級別,可通過如下兩種方式開啟內存加速查詢:
- vIndex 索引緩存 :使用固定的 512KiB 的內存空間緩存 vector index 數(shù)據(jù),減少一次 IO 磁盤操作,保持平均查詢效率穩(wěn)定在10-20微秒之間。
- xdb 整個文件緩存:將整個 xdb 文件全部加載到內存,內存占用等同于 xdb 文件大小,無磁盤 IO 操作,保持微秒級別的查詢效率。
3、Ip2region的使用
步驟:
1、生成ip2region.xdb文件,做好ip2region的相關配置
2、從請求中獲取用戶的ip地址
3、通過ip2redion.xdb中的對應關系找到用戶的ip對應的地點(格式:`國家|區(qū)域|省份|城市|運營商`,缺省的地域信息默認是0)
3.1、生成ip2region.xdb文件
下載ip2region的master分支
編譯安裝
通過 maven 來編譯可運行 jar 程序:
# cd 到 maker/java 根目錄 mvn clean compile package
然會會在當前目錄的 target 目錄下得到一個 ip2region-maker-{version}.jar 的打包文件。
數(shù)據(jù)生成
通過 java -jar ip2region-maker-{version}.jar 來生成 ip2region.xdb 二進制文件:
? java git:(java_xdb_maker) ? java -jar ./target/ip2region-maker-1.0.0.jar
ip2region xdb maker
java -jar ip2region-maker-{version}.jar [command options]
options:
--src string source ip text file path
--dst string destination binary xdb file path
例如,通過默認的 data/ip.merge.txt 原數(shù)據(jù),在當前目錄生成一個 ip2region.xdb 二進制文件:
在控制臺中輸入:java -jar ./target/ip2region-maker-1.0.0.jar --src=../../data/ip.merge.txt --dst=./ip2region.xdb
3.2、導入Ip2region的依賴
<!-- ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.3</version>
</dependency>
3.3、從請求中獲取用戶的ip
1.全局獲取HttpServletRequest的工具類
/**
* 全局獲取HttpServletRequest、HttpServletResponse的工具類
*/
public class HttpContextUtil {
private HttpContextUtil() {
}
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
}
public static HttpServletResponse getHttpServletResponse() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
}
}
2.從請求中獲取IP的類
public class IPUtil {
private static final String UNKNOWN = "unknown";
protected IPUtil() {
}
/**
* 獲取 IP地址
* 使用 Nginx等反向代理軟件, 則不能通過 request.getRemoteAddr()獲取 IP地址
* 如果使用了多級反向代理的話,X-Forwarded-For的值并不止一個,而是一串IP地址,
* X-Forwarded-For中第一個非 unknown的有效IP字符串,則為真實IP地址
*/
public static String getIpAddr(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();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}3.通過ip從ip2region.xdb中獲取用戶歸屬地
共有三種方法:
- 完全基于文件查詢
- 緩存VectorIndex索引
- 緩存整個xdb文件
public class AddressUtil {
public static String dbPath = "src/main/resources/ip2region/ip2region.xdb";
public static String region = "UNKOWN";
//方法一:完全基于文件的查詢
public static String getInfoByFie(String ip) throws IOException {
// 1、創(chuàng)建 searcher 對象
Searcher searcher = null;
try {
searcher = Searcher.newWithFileOnly(dbPath);
} catch (IOException e) {
System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e);
return "";
}
// 2、查詢
try {
//ip = "119.39.183.117";
long sTime = System.nanoTime();
region = searcher.searchByStr(ip);
long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
} catch (Exception e) {
System.out.printf("failed to search(%s): %s\n", ip, e);
}
return region;
// 備注:并發(fā)使用,每個線程需要創(chuàng)建一個獨立的 searcher 對象單獨使用。
}
//方法二:緩存 VectorIndex 索引
//我們可以提前從 xdb 文件中加載出來 VectorIndex 數(shù)據(jù),然后全局緩存,
// 每次創(chuàng)建 Searcher 對象的時候使用全局的 VectorIndex 緩存可以減少一次固定的 IO 操作,從而加速查詢,減少 IO 壓力。
public static String getInfoByVectorIndex(String ip) throws IOException {
// 1、從 dbPath 中預先加載 VectorIndex 緩存,并且把這個得到的數(shù)據(jù)作為全局變量,后續(xù)反復使用。
byte[] vIndex;
try {
vIndex = Searcher.loadVectorIndexFromFile(dbPath);
} catch (Exception e) {
System.out.printf("failed to load vector index from `%s`: %s\n", dbPath, e);
return "";
}
// 2、使用全局的 vIndex 創(chuàng)建帶 VectorIndex 緩存的查詢對象。
Searcher searcher;
try {
searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
} catch (Exception e) {
System.out.printf("failed to create vectorIndex cached searcher with `%s`: %s\n", dbPath, e);
return "";
}
// 2、查詢
try {
//ip = "119.39.183.117";
long sTime = System.nanoTime();
region = searcher.searchByStr(ip);
long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
} catch (Exception e) {
System.out.printf("failed to search(%s): %s\n", ip, e);
}
return region;
// 備注:每個線程需要單獨創(chuàng)建一個獨立的 Searcher 對象,但是都共享全局的制度 vIndex 緩存。
}
//方法三:緩存整個 xdb 數(shù)據(jù)
//我們也可以預先加載整個 ip2region.xdb 的數(shù)據(jù)到內存,
//然后基于這個數(shù)據(jù)創(chuàng)建查詢對象來實現(xiàn)完全基于文件的查詢,類似之前的 memory search。
public static String getInfoByBuffer(String ip) throws IOException {
// 1、從 dbPath 中預先加載 VectorIndex 緩存,并且把這個得到的數(shù)據(jù)作為全局變量,后續(xù)反復使用。
byte[] vIndex;
try {
vIndex = Searcher.loadVectorIndexFromFile(dbPath);
} catch (Exception e) {
System.out.printf("failed to load vector index from `%s`: %s\n", dbPath, e);
return "";
}
// 2、使用全局的 vIndex 創(chuàng)建帶 VectorIndex 緩存的查詢對象。
Searcher searcher;
try {
searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
} catch (Exception e) {
System.out.printf("failed to create vectorIndex cached searcher with `%s`: %s\n", dbPath, e);
return "";
}
// 2、查詢
try {
//ip = "119.39.183.117";
long sTime = System.nanoTime();
String region = searcher.searchByStr(ip);
long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost);
} catch (Exception e) {
System.out.printf("failed to search(%s): %s\n", ip, e);
}
return region;
// 備注:每個線程需要單獨創(chuàng)建一個獨立的 Searcher 對象,但是都共享全局的制度 vIndex 緩存。
}
public static void main(String[] args) throws IOException {
//1、完全基于文件查詢
String info1 = AddressUtil.getInfoByFie("203.15.235.101");
System.out.println(info1);
//2、緩存VectorIndex索引
String info2 = AddressUtil.getInfoByVectorIndex("203.15.235.101");
System.out.println(info2);
//3、緩存整個xdb文件
String info3 = AddressUtil.getInfoByVectorIndex("203.15.235.101");
System.out.println(info3);
}
}
4.測試結果

到此這篇關于Springboot整合ip2region實現(xiàn)用戶ip歸屬地獲取的文章就介紹到這了,更多相關Springboot ip2region獲取ip歸屬地內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Jmeter參數(shù)化獲取序列數(shù)據(jù)實現(xiàn)過程
這篇文章主要介紹了Jmeter參數(shù)化獲取序列數(shù)據(jù)實現(xiàn)過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-07-07
Spring的Aware接口實現(xiàn)及執(zhí)行順序詳解
這篇文章主要為大家介紹了Spring的Aware接口實現(xiàn)及執(zhí)行順序詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
Java實現(xiàn)兩人五子棋游戲(五) 判斷是否有一方勝出
這篇文章主要為大家詳細介紹了Java實現(xiàn)兩人五子棋游戲,判斷是否有一方勝出,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式
這篇文章主要介紹了獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08

