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ù),默認(rèn)的 region 信息都固定了格式:國家|區(qū)域|省份|城市|ISP,缺省的地域信息默認(rèn)是0。 region 信息支持完全自定義,例如:你
可以在 region 中追加特定業(yè)務(wù)需求的數(shù)據(jù),例如:GPS信息/國際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。
2.2、數(shù)據(jù)去重和壓縮
xdb 格式生成程序會自動去重和壓縮部分?jǐn)?shù)據(jù),默認(rèn)的全部 IP 數(shù)據(jù),生成的 ip2region.xdb 數(shù)據(jù)庫是 11MiB,隨著數(shù)據(jù)的詳細(xì)度增加數(shù)據(jù)庫的大小也慢慢增大。
2.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、Ip2region的使用
步驟:
1、生成ip2region.xdb文件,做好ip2region的相關(guān)配置
2、從請求中獲取用戶的ip地址
3、通過ip2redion.xdb中的對應(yīng)關(guān)系找到用戶的ip對應(yīng)的地點(格式:`國家|區(qū)域|省份|城市|運營商`,缺省的地域信息默認(rèn)是0)
3.1、生成ip2region.xdb文件
下載ip2region的master分支
編譯安裝
通過 maven 來編譯可運行 jar 程序:
# cd 到 maker/java 根目錄 mvn clean compile package
然會會在當(dāng)前目錄的 target 目錄下得到一個 ip2region-maker-{version}.jar 的打包文件。
數(shù)據(jù)生成
通過 java -jar ip2region-maker-{version}.jar 來生成 ip2region.xdb 二進(jìn)制文件:
? 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
例如,通過默認(rèn)的 data/ip.merge.txt 原數(shù)據(jù),在當(dāng)前目錄生成一個 ip2region.xdb 二進(jìn)制文件:
在控制臺中輸入:java -jar ./target/ip2region-maker-1.0.0.jar --src=../../data/ip.merge.txt --dst=./ip2region.xdb
3.2、導(dǎo)入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 中預(yù)先加載 VectorIndex 緩存,并且把這個得到的數(shù)據(jù)作為全局變量,后續(xù)反復(fù)使用。 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ù) //我們也可以預(yù)先加載整個 ip2region.xdb 的數(shù)據(jù)到內(nèi)存, //然后基于這個數(shù)據(jù)創(chuàng)建查詢對象來實現(xiàn)完全基于文件的查詢,類似之前的 memory search。 public static String getInfoByBuffer(String ip) throws IOException { // 1、從 dbPath 中預(yù)先加載 VectorIndex 緩存,并且把這個得到的數(shù)據(jù)作為全局變量,后續(xù)反復(fù)使用。 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.測試結(jié)果
到此這篇關(guān)于Springboot整合ip2region實現(xiàn)用戶ip歸屬地獲取的文章就介紹到這了,更多相關(guān)Springboot ip2region獲取ip歸屬地內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Jmeter參數(shù)化獲取序列數(shù)據(jù)實現(xiàn)過程
這篇文章主要介紹了Jmeter參數(shù)化獲取序列數(shù)據(jù)實現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07Spring的Aware接口實現(xiàn)及執(zhí)行順序詳解
這篇文章主要為大家介紹了Spring的Aware接口實現(xiàn)及執(zhí)行順序詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java實現(xiàn)兩人五子棋游戲(五) 判斷是否有一方勝出
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)兩人五子棋游戲,判斷是否有一方勝出,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03詳解Java 包掃描實現(xiàn)和應(yīng)用(Jar篇)
這篇文章主要介紹了詳解Java 包掃描實現(xiàn)和應(yīng)用(Jar篇),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式
這篇文章主要介紹了獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08