Springboot整合ip2region實(shí)現(xiàn)用戶ip歸屬地獲取
1、 Ip2region 是什么
ip2region - 是一個(gè)離線IP地址定位庫和IP定位數(shù)據(jù)管理框架,10微秒級(jí)別的查詢效率,提供了眾多主流編程語言的 xdb 數(shù)據(jù)生成和查詢客戶端實(shí)現(xiàn)。
2、Ip2region 特性
2.1、IP 數(shù)據(jù)管理框架
xdb 支持億級(jí)別的 IP 數(shù)據(jù)段行數(shù),默認(rèn)的 region 信息都固定了格式:國(guó)家|區(qū)域|省份|城市|ISP,缺省的地域信息默認(rèn)是0。 region 信息支持完全自定義,例如:你
可以在 region 中追加特定業(yè)務(wù)需求的數(shù)據(jù),例如:GPS信息/國(guó)際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。
2.2、數(shù)據(jù)去重和壓縮
xdb 格式生成程序會(huì)自動(dòng)去重和壓縮部分?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)時(shí)間在十微秒級(jí)別,可通過如下兩種方式開啟內(nèi)存加速查詢:
- vIndex 索引緩存 :使用固定的 512KiB 的內(nèi)存空間緩存 vector index 數(shù)據(jù),減少一次 IO 磁盤操作,保持平均查詢效率穩(wěn)定在10-20微秒之間。
- xdb 整個(gè)文件緩存:將整個(gè) xdb 文件全部加載到內(nèi)存,內(nèi)存占用等同于 xdb 文件大小,無磁盤 IO 操作,保持微秒級(jí)別的查詢效率。
3、Ip2region的使用
步驟:
1、生成ip2region.xdb文件,做好ip2region的相關(guān)配置
2、從請(qǐng)求中獲取用戶的ip地址
3、通過ip2redion.xdb中的對(duì)應(yīng)關(guān)系找到用戶的ip對(duì)應(yīng)的地點(diǎn)(格式:`國(guó)家|區(qū)域|省份|城市|運(yùn)營(yíng)商`,缺省的地域信息默認(rèn)是0)
3.1、生成ip2region.xdb文件
下載ip2region的master分支
編譯安裝
通過 maven 來編譯可運(yùn)行 jar 程序:
# cd 到 maker/java 根目錄 mvn clean compile package
然會(huì)會(huì)在當(dāng)前目錄的 target 目錄下得到一個(gè) 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)前目錄生成一個(gè) ip2region.xdb 二進(jìn)制文件:
在控制臺(tái)中輸入: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、從請(qǐng)求中獲取用戶的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.從請(qǐng)求中獲取IP的類
public class IPUtil { private static final String UNKNOWN = "unknown"; protected IPUtil() { } /** * 獲取 IP地址 * 使用 Nginx等反向代理軟件, 則不能通過 request.getRemoteAddr()獲取 IP地址 * 如果使用了多級(jí)反向代理的話,X-Forwarded-For的值并不止一個(gè),而是一串IP地址, * X-Forwarded-For中第一個(gè)非 unknown的有效IP字符串,則為真實(shí)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索引
- 緩存整個(gè)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 對(duì)象 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ā)使用,每個(gè)線程需要?jiǎng)?chuàng)建一個(gè)獨(dú)立的 searcher 對(duì)象單獨(dú)使用。 } //方法二:緩存 VectorIndex 索引 //我們可以提前從 xdb 文件中加載出來 VectorIndex 數(shù)據(jù),然后全局緩存, // 每次創(chuàng)建 Searcher 對(duì)象的時(shí)候使用全局的 VectorIndex 緩存可以減少一次固定的 IO 操作,從而加速查詢,減少 IO 壓力。 public static String getInfoByVectorIndex(String ip) throws IOException { // 1、從 dbPath 中預(yù)先加載 VectorIndex 緩存,并且把這個(gè)得到的數(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 緩存的查詢對(duì)象。 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; // 備注:每個(gè)線程需要單獨(dú)創(chuàng)建一個(gè)獨(dú)立的 Searcher 對(duì)象,但是都共享全局的制度 vIndex 緩存。 } //方法三:緩存整個(gè) xdb 數(shù)據(jù) //我們也可以預(yù)先加載整個(gè) ip2region.xdb 的數(shù)據(jù)到內(nèi)存, //然后基于這個(gè)數(shù)據(jù)創(chuàng)建查詢對(duì)象來實(shí)現(xiàn)完全基于文件的查詢,類似之前的 memory search。 public static String getInfoByBuffer(String ip) throws IOException { // 1、從 dbPath 中預(yù)先加載 VectorIndex 緩存,并且把這個(gè)得到的數(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 緩存的查詢對(duì)象。 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; // 備注:每個(gè)線程需要單獨(dú)創(chuàng)建一個(gè)獨(dú)立的 Searcher 對(duì)象,但是都共享全局的制度 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、緩存整個(gè)xdb文件 String info3 = AddressUtil.getInfoByVectorIndex("203.15.235.101"); System.out.println(info3); } }
4.測(cè)試結(jié)果
到此這篇關(guān)于Springboot整合ip2region實(shí)現(xiàn)用戶ip歸屬地獲取的文章就介紹到這了,更多相關(guān)Springboot ip2region獲取ip歸屬地內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用TCP套接字實(shí)現(xiàn)多人聊天功能詳解
這篇文章主要介紹了Java使用TCP套接字實(shí)現(xiàn)多人聊天功能,結(jié)合實(shí)例形式詳細(xì)分析了java使用socket通信實(shí)現(xiàn)tcp協(xié)議下的聊天功能客戶端與服務(wù)器端相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-10-10Jmeter參數(shù)化獲取序列數(shù)據(jù)實(shí)現(xiàn)過程
這篇文章主要介紹了Jmeter參數(shù)化獲取序列數(shù)據(jù)實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07Spring的Aware接口實(shí)現(xiàn)及執(zhí)行順序詳解
這篇文章主要為大家介紹了Spring的Aware接口實(shí)現(xiàn)及執(zhí)行順序詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java實(shí)現(xiàn)兩人五子棋游戲(五) 判斷是否有一方勝出
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)兩人五子棋游戲,判斷是否有一方勝出,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03詳解Java 包掃描實(shí)現(xiàn)和應(yīng)用(Jar篇)
這篇文章主要介紹了詳解Java 包掃描實(shí)現(xiàn)和應(yīng)用(Jar篇),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07獲取Spring的上下文環(huán)境ApplicationContext的最簡(jiǎn)單方式
這篇文章主要介紹了獲取Spring的上下文環(huán)境ApplicationContext的最簡(jiǎn)單方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08