diff --git a/binding/java/ReadMe.md b/binding/java/ReadMe.md index 8f8e7fc..44231f4 100644 --- a/binding/java/ReadMe.md +++ b/binding/java/ReadMe.md @@ -15,35 +15,29 @@ ```java import org.lionsoul.ip2region.xdb.Searcher; + import java.io.*; import java.util.concurrent.TimeUnit; public class SearcherTest { public static void main(String[] args) { - // 1、创建 searcher 对象 String dbPath = "ip2region.xdb file path"; - 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; - } + String ip = "1.2.3.4"; - // 2、查询 - try { - String ip = "1.2.3.4"; + // 1、创建 searcher 对象 + try (Searcher searcher = Searcher.newWithFileOnly(dbPath)) { + // 2、查询 long sTime = System.nanoTime(); String region = searcher.search(ip); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); + long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime); System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); + } catch (IOException e) { + System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e); } catch (Exception e) { System.out.printf("failed to search(%s): %s\n", ip, e); } + // 3、关闭资源(这里通过 try-with-resources 自动关闭) - // 3、关闭资源 - searcher.close(); - // 备注:并发使用,每个线程需要创建一个独立的 searcher 对象单独使用。 } } @@ -54,12 +48,14 @@ public class SearcherTest { 我们可以提前从 `xdb` 文件中加载出来 `VectorIndex` 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。 ```java import org.lionsoul.ip2region.xdb.Searcher; + import java.io.*; import java.util.concurrent.TimeUnit; public class SearcherTest { public static void main(String[] args) { String dbPath = "ip2region.xdb file path"; + String ip = "1.2.3.4"; // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。 byte[] vIndex; @@ -71,27 +67,17 @@ public class SearcherTest { } // 2、使用全局的 vIndex 创建带 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; - } - - // 3、查询 - try { - String ip = "1.2.3.4"; + try (Searcher searcher = Searcher.newWithVectorIndex(dbPath, vIndex)) { long sTime = System.nanoTime(); String region = searcher.search(ip); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); + long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime); System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); + } catch (IOException e) { + System.out.printf("failed to create vectorIndex cached searcher with `%s`: %s\n", dbPath, e); } catch (Exception e) { System.out.printf("failed to search(%s): %s\n", ip, e); } - - // 4、关闭资源 - searcher.close(); + // 3、关闭资源(这里通过 try-with-resources 自动关闭) // 备注:每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局的制度 vIndex 缓存。 } @@ -103,12 +89,14 @@ public class SearcherTest { 我们也可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询,类似之前的 memory search。 ```java import org.lionsoul.ip2region.xdb.Searcher; + import java.io.*; import java.util.concurrent.TimeUnit; public class SearcherTest { public static void main(String[] args) { String dbPath = "ip2region.xdb file path"; + String ip = "1.2.3.4"; // 1、从 dbPath 加载整个 xdb 到内存。 byte[] cBuff; @@ -123,22 +111,21 @@ public class SearcherTest { Searcher searcher; try { searcher = Searcher.newWithBuffer(cBuff); - } catch (Exception e) { + } catch (IOException e) { System.out.printf("failed to create content cached searcher: %s\n", e); return; } // 3、查询 try { - String ip = "1.2.3.4"; long sTime = System.nanoTime(); String region = searcher.search(ip); - long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); + long cost = TimeUnit.NANOSECONDS.toMicros(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); } - + // 4、关闭资源 - 该 searcher 对象可以安全用于并发,等整个服务关闭的时候再关闭 searcher // searcher.close(); diff --git a/binding/java/pom.xml b/binding/java/pom.xml index 43fe239..7ad2492 100644 --- a/binding/java/pom.xml +++ b/binding/java/pom.xml @@ -41,6 +41,7 @@ UTF-8 UTF-8 + 1.8 1.6 1.6 @@ -112,6 +113,15 @@ + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/SearchTest.java b/binding/java/src/main/java/org/lionsoul/ip2region/SearchTest.java index 5c70266..e139d57 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/SearchTest.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/SearchTest.java @@ -8,7 +8,10 @@ package org.lionsoul.ip2region; import org.lionsoul.ip2region.xdb.Searcher; -import java.io.*; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.concurrent.TimeUnit; public class SearchTest { @@ -65,7 +68,7 @@ public class SearchTest { } } - if (dbPath.length() < 1) { + if (dbPath.isEmpty()) { System.out.print("java -jar ip2region-{version}.jar search [command options]\n"); System.out.print("options:\n"); System.out.print(" --db string ip2region binary xdb file path\n"); @@ -73,32 +76,30 @@ public class SearchTest { return; } - Searcher searcher = createSearcher(dbPath, cachePolicy); - final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); - System.out.printf("ip2region xdb searcher test program, cachePolicy: %s\ntype 'quit' to exit\n", cachePolicy); - while ( true ) { - System.out.print("ip2region>> "); - String line = reader.readLine().trim(); - if ( line.length() < 2 ) { - continue; - } + try (Searcher searcher = createSearcher(dbPath, cachePolicy); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { + System.out.printf("ip2region xdb searcher test program, cachePolicy: %s\ntype 'quit' to exit\n", cachePolicy); + while (true) { + System.out.print("ip2region>> "); + String line = reader.readLine().trim(); + if (line.length() < 2) { + continue; + } - if ( line.equalsIgnoreCase("quit") ) { - break; - } + if ("quit".equalsIgnoreCase(line)) { + break; + } - try { - double sTime = System.nanoTime(); - String region = searcher.search(line); - 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("{err: %s, ioCount: %d}\n", e, searcher.getIOCount()); + try { + double sTime = System.nanoTime(); + String region = searcher.search(line); + 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("{err: %s, ioCount: %d}\n", e, searcher.getIOCount()); + } } } - - reader.close(); - searcher.close(); System.out.println("searcher test program exited, thanks for trying"); } @@ -121,19 +122,23 @@ public class SearchTest { String key = r.substring(2, sIdx); String val = r.substring(sIdx + 1); - if ("db".equals(key)) { - dbPath = val; - } else if ("src".equals(key)) { - srcPath = val; - } else if ("cache-policy".equals(key)) { - cachePolicy = val; - } else { - System.out.printf("undefined option `%s`\n", r); - return; + switch (key) { + case "db": + dbPath = val; + break; + case "src": + srcPath = val; + break; + case "cache-policy": + cachePolicy = val; + break; + default: + System.out.printf("undefined option `%s`\n", r); + return; } } - if (dbPath.length() < 1 || srcPath.length() < 1) { + if (dbPath.isEmpty() || srcPath.isEmpty()) { System.out.print("java -jar ip2region-{version}.jar bench [command options]\n"); System.out.print("options:\n"); System.out.print(" --db string ip2region binary xdb file path\n"); @@ -142,61 +147,59 @@ public class SearchTest { return; } - Searcher searcher = createSearcher(dbPath, cachePolicy); long count = 0, costs = 0, tStart = System.nanoTime(); - String line; - final BufferedReader reader = new BufferedReader(new FileReader(srcPath)); - while ((line = reader.readLine()) != null) { - String l = line.trim(); - String[] ps = l.split("\\|", 3); - if (ps.length != 3) { - System.out.printf("invalid ip segment `%s`\n", l); - return; - } - - long sip; - try { - sip = Searcher.checkIP(ps[0]); - } catch (Exception e) { - System.out.printf("check start ip `%s`: %s\n", ps[0], e); - return; - } - - long eip; - try { - eip = Searcher.checkIP(ps[1]); - } catch (Exception e) { - System.out.printf("check end ip `%s`: %s\n", ps[1], e); - return; - } - - if (sip > eip) { - System.out.printf("start ip(%s) should not be greater than end ip(%s)\n", ps[0], ps[1]); - return; - } - - long mip = (sip + eip) >> 1; - for (final long ip : new long[]{sip, (sip + mip) >> 1, mip, (mip + eip) >> 1, eip}) { - long sTime = System.nanoTime(); - String region = searcher.search(ip); - costs += System.nanoTime() - sTime; - - // check the region info - if (!ps[2].equals(region)) { - System.out.printf("failed search(%s) with (%s != %s)\n", Searcher.long2ip(ip), region, ps[2]); + try (Searcher searcher = createSearcher(dbPath, cachePolicy); + BufferedReader reader = new BufferedReader(new FileReader(srcPath))) { + String line; + while ((line = reader.readLine()) != null) { + String l = line.trim(); + String[] ps = l.split("\\|", 3); + if (ps.length != 3) { + System.out.printf("invalid ip segment `%s`\n", l); return; } - count++; + long sip; + try { + sip = Searcher.checkIP(ps[0]); + } catch (Exception e) { + System.out.printf("check start ip `%s`: %s\n", ps[0], e); + return; + } + + long eip; + try { + eip = Searcher.checkIP(ps[1]); + } catch (Exception e) { + System.out.printf("check end ip `%s`: %s\n", ps[1], e); + return; + } + + if (sip > eip) { + System.out.printf("start ip(%s) should not be greater than end ip(%s)\n", ps[0], ps[1]); + return; + } + + long mip = (sip + eip) >> 1; + for (final long ip : new long[]{sip, (sip + mip) >> 1, mip, (mip + eip) >> 1, eip}) { + long sTime = System.nanoTime(); + String region = searcher.search(ip); + costs += System.nanoTime() - sTime; + + // check the region info + if (!ps[2].equals(region)) { + System.out.printf("failed search(%s) with (%s != %s)\n", Searcher.long2ip(ip), region, ps[2]); + return; + } + + count++; + } } } - - reader.close(); - searcher.close(); long took = System.nanoTime() - tStart; System.out.printf("Bench finished, {cachePolicy: %s, total: %d, took: %ds, cost: %d μs/op}\n", cachePolicy, count, TimeUnit.NANOSECONDS.toSeconds(took), - count == 0 ? 0 : TimeUnit.NANOSECONDS.toMicros(costs/count)); + count == 0 ? 0 : TimeUnit.NANOSECONDS.toMicros(costs / count)); } public static void main(String[] args) { diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/UtilTest.java b/binding/java/src/main/java/org/lionsoul/ip2region/UtilTest.java index aba7779..06e806c 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/UtilTest.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/UtilTest.java @@ -12,7 +12,7 @@ public class UtilTest { public static void testIP2Long() { String ip = "1.2.3.4"; - long ipAddr = 0; + long ipAddr; try { ipAddr = Searcher.checkIP(ip); } catch (Exception e) { diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java index a55cedc..b38f918 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Header.java @@ -24,13 +24,14 @@ public class Header { buffer = buff; } - @Override public String toString() { + @Override + public String toString() { return "{" + - "Version: " + version + ',' + - "IndexPolicy: " + indexPolicy + ',' + - "CreatedAt: " + createdAt + ',' + - "StartIndexPtr: " + startIndexPtr + ',' + - "EndIndexPtr: " + endIndexPtr + - '}'; + "Version: " + version + ',' + + "IndexPolicy: " + indexPolicy + ',' + + "CreatedAt: " + createdAt + ',' + + "StartIndexPtr: " + startIndexPtr + ',' + + "EndIndexPtr: " + endIndexPtr + + '}'; } } diff --git a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java index f441036..4912042 100644 --- a/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java +++ b/binding/java/src/main/java/org/lionsoul/ip2region/xdb/Searcher.java @@ -9,10 +9,12 @@ package org.lionsoul.ip2region.xdb; // @Date 2022/06/23 +import java.io.Closeable; import java.io.IOException; import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; -public class Searcher { +public class Searcher implements Closeable { // constant defined copied from the xdb maker public static final int HeaderInfoLength = 256; public static final int VectorIndexRows = 256; @@ -61,6 +63,7 @@ public class Searcher { } } + @Override public void close() throws IOException { if (this.handle != null) { this.handle.close(); @@ -135,7 +138,7 @@ public class Searcher { // load and return the region data final byte[] regionBuff = new byte[dataLen]; read(dataPtr, regionBuff); - return new String(regionBuff, "utf-8"); + return new String(regionBuff, StandardCharsets.UTF_8); } protected void read(int offset, byte[] buffer) throws IOException { @@ -167,10 +170,9 @@ public class Searcher { } public static Header loadHeaderFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - final Header header = loadHeader(handle); - handle.close(); - return header; + try (RandomAccessFile handle = new RandomAccessFile(dbPath, "r")) { + return loadHeader(handle); + } } public static byte[] loadVectorIndex(RandomAccessFile handle) throws IOException { @@ -186,10 +188,9 @@ public class Searcher { } public static byte[] loadVectorIndexFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - final byte[] vIndex = loadVectorIndex(handle); - handle.close(); - return vIndex; + try (RandomAccessFile handle = new RandomAccessFile(dbPath, "r")) { + return loadVectorIndex(handle); + } } public static byte[] loadContent(RandomAccessFile handle) throws IOException { @@ -204,10 +205,9 @@ public class Searcher { } public static byte[] loadContentFromFile(String dbPath) throws IOException { - final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); - final byte[] content = loadContent(handle); - handle.close(); - return content; + try (RandomAccessFile handle = new RandomAccessFile(dbPath, "r")) { + return loadContent(handle); + } } // --- End cache load util function @@ -217,26 +217,26 @@ public class Searcher { /* get an int from a byte array start from the specified offset */ public static long getIntLong(byte[] b, int offset) { return ( - ((b[offset++] & 0x000000FFL)) | - ((b[offset++] << 8) & 0x0000FF00L) | - ((b[offset++] << 16) & 0x00FF0000L) | - ((b[offset ] << 24) & 0xFF000000L) + ((b[offset++] & 0x000000FFL)) | + ((b[offset++] << 8) & 0x0000FF00L) | + ((b[offset++] << 16) & 0x00FF0000L) | + ((b[offset ] << 24) & 0xFF000000L) ); } public static int getInt(byte[] b, int offset) { return ( - ((b[offset++] & 0x000000FF)) | - ((b[offset++] << 8) & 0x0000FF00) | - ((b[offset++] << 16) & 0x00FF0000) | - ((b[offset ] << 24) & 0xFF000000) + ((b[offset++] & 0x000000FF)) | + ((b[offset++] << 8) & 0x0000FF00) | + ((b[offset++] << 16) & 0x00FF0000) | + ((b[offset ] << 24) & 0xFF000000) ); } public static int getInt2(byte[] b, int offset) { return ( - ((b[offset++] & 0x000000FF)) | - ((b[offset ] << 8) & 0x0000FF00) + ((b[offset++] & 0x000000FF)) | + ((b[offset ] << 8) & 0x0000FF00) ); } @@ -244,7 +244,7 @@ public class Searcher { public static String long2ip( long ip ) { return String.valueOf((ip >> 24) & 0xFF) + '.' + - ((ip >> 16) & 0xFF) + '.' + ((ip >> 8) & 0xFF) + '.' + ((ip) & 0xFF); + ((ip >> 16) & 0xFF) + '.' + ((ip >> 8) & 0xFF) + '.' + ((ip) & 0xFF); } public static final byte[] shiftIndex = {24, 16, 8, 0}; @@ -269,4 +269,4 @@ public class Searcher { return ipDst & 0xFFFFFFFFL; } -} \ No newline at end of file +}