Merge branch 'fr_java_service'

This commit is contained in:
lionsoul2014 2025-12-01 13:03:50 +08:00
commit 4a4c8de636
12 changed files with 802 additions and 4 deletions

View File

@ -7,10 +7,48 @@
<dependency> <dependency>
<groupId>org.lionsoul</groupId> <groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId> <artifactId>ip2region</artifactId>
<version>3.1.1</version> <version>3.2.0</version>
</dependency> </dependency>
``` ```
### 关于查询服务
`3.2.0` 版本开始提供了一个双协议兼容且并发安全的 `Ip2Region` 查询服务,**建议优先使用该方式来进行查询调用**,具体使用方式如下:
```java
// 1, 创建 v4 的配置:指定缓存策略和 v4 的 xdb 文件路径
final Config v4Config = Config.custom()
.setCachePolicy(Config.VIndexCache) // 指定缓存策略: NoCache / VIndexCache / BufferCache
.setSeachers(15) // 设置初始化的查询器数量
.setXdbPath("ip2region v4 xdb path") // 设置 v4 xdb 文件的路径
.asV4(); // 指定为 v4 配置
// 2, 创建 v6 的配置:指定缓存策略和 v6 的 xdb 文件路径
final Config v6Config = Config.custom()
.setCachePolicy(Config.VIndexCache) // 指定缓存策略: NoCache / VIndexCache / BufferCache
.setSeachers(15) // 设置初始化的查询器数量
.setXdbPath("ip2region v6 xdb path") // 设置 v6 xdb 文件的路径
.asV6(); // 指定为 v6 配置
// 3通过上述配置创建 Ip2Region 查询服务
final Ip2Region ip2Region = Ip2Region.create(v4Config, v6Config);
// 4导出 ip2region 服务进行双版本的IP地址的并发查询例如
final String v4Region = ip2Region.search("113.92.157.29"); // 进行 IPv4 查询
final String v6Region = ip2Region.search("240e:3b7:3272:d8d0:db09:c067:8d59:539e"); // 进行 IPv6 查询
// 5在服务需要关闭的时候同时关闭 ip2region 查询服务
ip2Region.close();
```
关于 `Ip2Region`查询服务的信息:
1. 该查询服务的 API 并发安全且同时支持 `IPv4``Ipv6` 的地址,内部实现会自动判断。
2. v4 和 v6 的配置需要单独创建,可以给 v4 和 v6 设置使用不同的缓存策略,也可以指定其中一个为 `null` 则该版本的 IP 地址查询都会返回 `null`
3. 请结合您项目的并发数给 `setSearchers` 一个合适的查询器数量,默认为 20 个,这个值在运行过程中是固定的,每次查询会从池子里租借一个查询器来完成查询操作,查询完成后再归还回去,如果租借的时候池子已经空了则等待直到有可用的查询器来完成查询服务,租借的锁是使用的 `ReentrantLock` 来管理,也可以通过如下方式来设置 `Ip2Region` 查询服务使用公平锁:
```java
final Ip2Region ip2region = Ip2Region.create(v4Config, v6Config, true);
```
4. 如果配置设置的缓存策略为 `Config.BufferCache``全内存缓存` 则默认会使用单实例的内存查询器,该实现天生并发安全,此时通过 `setSearchers` 指定的查询器数量无效。
5. 如果 `ip2region` 查询器在提供服务期间,调用 close 默认会最大等待 10 秒钟来等待尽量多的查询器归还。
### 关于查询 API ### 关于查询 API
定位信息查询 API 的原型为: 定位信息查询 API 的原型为:
```java ```java

View File

@ -4,7 +4,7 @@
<groupId>org.lionsoul</groupId> <groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId> <artifactId>ip2region</artifactId>
<version>3.1.1</version> <version>3.2.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>ip2region</name> <name>ip2region</name>
@ -116,7 +116,7 @@
<configuration> <configuration>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.lionsoul.ip2region.SearchApp</mainClass> <mainClass>org.lionsoul.ip2region.SearcherTest</mainClass>
</transformer> </transformer>
</transformers> </transformers>
</configuration> </configuration>

View File

@ -0,0 +1,83 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
package org.lionsoul.ip2region;
import java.io.IOException;
import org.lionsoul.ip2region.xdb.Header;
import org.lionsoul.ip2region.xdb.LongByteArray;
import org.lionsoul.ip2region.xdb.Version;
import org.lionsoul.ip2region.xdb.XdbException;
/**
* ip2region config class
* @Author Lion <chenxin619315@gmail.com>
* @Date 2025/11/20
*/
public class Config {
// cache policy consts
public static final int NoCache = 0;
public static final int VIndexCache = 1;
public static final int BufferCache = 2;
// search cache policy
public final int cachePolicy;
public final Version ipVersion;
// xdb file path
public final String xdbPath;
public final Header header;
public final byte[] vIndex;
public final LongByteArray cBuffer;
public final int searchers;
// config builder
public static ConfigBuilder custom() {
return new ConfigBuilder();
}
protected Config(int cachePolicy, Version ipVersion, String xdbPath,
Header header, byte[] vIndex, LongByteArray cBuffer, int searchers) throws IOException, XdbException {
this.cachePolicy = cachePolicy;
this.ipVersion = ipVersion;
this.xdbPath = xdbPath;
this.header = header;
this.vIndex = vIndex;
this.cBuffer = cBuffer;
final Version xVersion = Version.fromHeader(header);
// verify the ip version (ipVersion and the version of the xdb file should be the same)
if (header.ipVersion != ipVersion.id) {
throw new XdbException("ip verison not match: xdb file "
+ xdbPath + " (" + xVersion.name + "), as " + ipVersion.name + " expected");
}
this.searchers = searchers;
}
@Override public String toString() {
final StringBuffer sb = new StringBuffer();
sb.append('{');
sb.append("cache_policy:").append(cachePolicy).append(',');
sb.append("version:").append(ipVersion.toString()).append(',');
sb.append("xdb_path:").append(xdbPath).append(',');
sb.append("header:").append(header.toString()).append(',');
if (vIndex == null) {
sb.append("v_index: null, ");
} else {
sb.append("v_index: {bytes: ").append(vIndex.length).append("},");
}
if (cBuffer == null) {
sb.append("c_buffer: null, ");
} else {
sb.append("c_buffer: {bytes: ").append(cBuffer.length()).append("},");
}
sb.append("searchers:").append(searchers);
sb.append('}');
return sb.toString();
}
}

View File

@ -0,0 +1,83 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
package org.lionsoul.ip2region;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.lionsoul.ip2region.xdb.Header;
import org.lionsoul.ip2region.xdb.LongByteArray;
import org.lionsoul.ip2region.xdb.Searcher;
import org.lionsoul.ip2region.xdb.Version;
import org.lionsoul.ip2region.xdb.XdbException;
/**
* ip2region config builder
* @Author Lion <chenxin619315@gmail.com>
* @Date 2025/11/20
*/
public class ConfigBuilder {
// cache policy
private int cachePolicy = Config.VIndexCache;
// xdb file path
private String xdbPath = null;
// searchers
private int searchers = 20;
public ConfigBuilder() {}
public ConfigBuilder(String xdbPath) {
this.xdbPath = xdbPath;
}
public ConfigBuilder setCachePolicy(int cachePolicy) {
this.cachePolicy = cachePolicy;
return this;
}
public ConfigBuilder setXdbPath(String xdbPath) {
this.xdbPath = xdbPath;
return this;
}
public ConfigBuilder setSeachers(int searchers) {
this.searchers = searchers;
return this;
}
private Config build(Version ipVersion) throws IOException, XdbException {
// load the header and the cache buffer
final RandomAccessFile raf = new RandomAccessFile(xdbPath, "r");
// 1, verify the xdb
Searcher.verify(raf);
// 2, load the header
final Header header = Searcher.loadHeader(raf);
// 3, check and load the vector index buffer
final byte[] vIndex = cachePolicy == Config.VIndexCache ? Searcher.loadVectorIndex(raf) : null;
// 4, check and load the content buffer
final LongByteArray cBuffer = cachePolicy == Config.BufferCache ? Searcher.loadContent(raf) : null;
raf.close();
return new Config(cachePolicy, ipVersion, xdbPath, header, vIndex, cBuffer, searchers);
}
// build the final #Config instance for IPv4
public Config asV4() throws IOException, XdbException {
return build(Version.IPv4);
}
// build the final #Config instance for IPv6
public Config asV6() throws IOException, XdbException {
return build(Version.IPv6);
}
}

View File

@ -0,0 +1,171 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
package org.lionsoul.ip2region;
import java.io.IOException;
import org.lionsoul.ip2region.xdb.InetAddressException;
import org.lionsoul.ip2region.xdb.Searcher;
import org.lionsoul.ip2region.xdb.Util;
import org.lionsoul.ip2region.xdb.XdbException;
/**
* ip2region searcher manager service to provider:
* 1. Unified query interface for IPv4 and IPv6 address.
* 2. Concurrency search support.
*
* @Author Lion <chenxin619315@gmail.com>
* Date 2025/11/21
*/
public class Ip2Region {
/* v4 pool for cache policy vIndex or NoCache */
private final SearcherPool v4Pool;
/* v4 xdb searcher for cache policy cBuffer */
private final Searcher v4InMemSearcher;
/* v6 pool for cache policy vIndex or NoCache*/
private final SearcherPool v6Pool;
/* v6 xdb searcher for cache policy cBuffer */
private final Searcher v6InMemSearcher;
public static final Ip2Region create(final Config v4Config, final Config v6Config) throws IOException {
return new Ip2Region(v4Config, v6Config).init();
}
public static final Ip2Region create(final String v4XdbPath, final String v6XdbPath) throws IOException, XdbException {
return new Ip2Region(v4XdbPath, v6XdbPath).init();
}
/**
* init the ip2reigon with two xdb file path and default cachePolicy vIndex.
* set it to null to disabled the search for specified version
*
* @param v4XdbPath
* @param v6XdbPath
* @throws XdbException
* @throws IOException
*/
protected Ip2Region(String v4XdbPath, String v6XdbPath) throws IOException, XdbException {
this(
v4XdbPath == null ? null : Config.custom().setXdbPath(v4XdbPath).asV4(),
v6XdbPath == null ? null : Config.custom().setXdbPath(v6XdbPath).asV6()
);
}
/**
* init the ip2region with specified config.
* set it to null for disabled the search for specified version
*
* @param v4Config
* @param v6Config
* @throws IOException
*/
protected Ip2Region(Config v4Config, Config v6Config) throws IOException {
if (v4Config == null) {
// @Note: with IPv4 disabled ?
this.v4InMemSearcher = null;
this.v4Pool = null;
} else if (v4Config.cachePolicy == Config.BufferCache) {
this.v4InMemSearcher = Searcher.newWithBuffer(v4Config.ipVersion, v4Config.cBuffer);
this.v4Pool = null;
} else {
this.v4InMemSearcher = null;
this.v4Pool = new SearcherPool(v4Config);
}
if (v6Config == null) {
// @Note: with IPv6 disabled ?
this.v6InMemSearcher = null;
this.v6Pool = null;
} else if (v6Config.cachePolicy == Config.BufferCache) {
this.v6InMemSearcher = Searcher.newWithBuffer(v6Config.ipVersion, v6Config.cBuffer);
this.v6Pool = null;
} else {
this.v6InMemSearcher = null;
this.v6Pool = new SearcherPool(v6Config);
}
}
// init the current ip2region service
protected Ip2Region init() throws IOException {
if (v4Pool != null) {
v4Pool.init();
}
if (v6Pool != null) {
v6Pool.init();
}
return this;
}
public String search(String ipString) throws InetAddressException, IOException, InterruptedException {
return search(Util.parseIP(ipString));
}
public String search(byte[] ipBytes) throws InetAddressException, IOException, InterruptedException {
if (ipBytes.length == 4) {
return v4Search(ipBytes);
} else if (ipBytes.length == 16) {
return v6Search(ipBytes);
} else {
throw new InetAddressException("invalid byte ip address with length=" + ipBytes.length);
}
}
protected String v4Search(final byte[] ipBytes) throws IOException, InetAddressException, InterruptedException {
if (v4InMemSearcher != null) {
return v4InMemSearcher.search(ipBytes);
}
// IPv4 search is disabled
if (v4Pool == null) {
return null;
}
final Searcher searcher = v4Pool.borrowSearcher();
try {
return searcher.search(ipBytes);
} finally {
v4Pool.returnSearcher(searcher);
}
}
protected String v6Search(final byte[] ipBytes) throws IOException, InetAddressException, InterruptedException {
if (v6InMemSearcher != null) {
return v6InMemSearcher.search(ipBytes);
}
// IPv6 search is disabled
if (v6Pool == null) {
return null;
}
final Searcher searcher = v6Pool.borrowSearcher();
try {
return searcher.search(ipBytes);
} finally {
v6Pool.returnSearcher(searcher);
}
}
public void close() throws InterruptedException {
close(10000);
}
public void close(long timeoutMillis) throws InterruptedException {
if (v4Pool != null) {
v4Pool.close(timeoutMillis);
}
if (v6Pool != null) {
v6Pool.close(timeoutMillis);
}
}
}

View File

@ -0,0 +1,123 @@
// Copyright 2022 The Ip2Region Authors. All rights reserved.
// Use of this source code is governed by a Apache2.0-style
// license that can be found in the LICENSE file.
package org.lionsoul.ip2region;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import org.lionsoul.ip2region.xdb.Searcher;
/**
* ip2region searcher pool manager to provider Concurrency search support.
*
* @author Leon<chenxin619315@gmail.com>
* Date 2025/11/21
*/
public class SearcherPool {
// config instance
public final Config config;
// searcher pool
private final Queue<Searcher> pool;
// lock & conditions
private final ReentrantLock lock;
private final Condition emptyCondition;
private final Condition fullCondition;
// searcher numbers that was loaned out
private int loanCount;
// static method to create and init the searcher pool
public static final SearcherPool create(final Config config) throws IOException {
return new SearcherPool(config).init();
}
public static final SearcherPool create(final Config config, boolean fair) throws IOException {
return new SearcherPool(config, fair).init();
}
protected SearcherPool(Config config) throws IOException {
this(config, false);
}
protected SearcherPool(Config config, boolean fair) {
assert config.searchers > 0;
this.config = config;
this.pool = new LinkedList<>();
this.lock = new ReentrantLock(fair);
this.emptyCondition = this.lock.newCondition();
this.fullCondition = this.lock.newCondition();
this.loanCount = 0;
}
protected SearcherPool init() throws IOException {
// create the searchers
for (int i = pool.size(); i < config.searchers; i++) {
final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer);
pool.add(searcher);
}
return this;
}
public Searcher borrowSearcher() throws InterruptedException {
lock.lock();
try {
while (pool.isEmpty()) {
emptyCondition.await();
}
loanCount++;
return pool.poll();
} finally {
lock.unlock();
}
}
public void returnSearcher(final Searcher searcher) {
lock.lock();
try {
pool.add(searcher);
loanCount--;
emptyCondition.signal();
// check and signal the full condition.
// pool close
if (loanCount == 0) {
fullCondition.signal();
}
} finally {
lock.unlock();
}
}
// close the searcher pool
public void close() throws InterruptedException {
close(10000);
}
public void close(long timeoutMillis) throws InterruptedException {
lock.lock();
try {
while (loanCount > 0) {
fullCondition.wait(timeoutMillis);
}
final Iterator<Searcher> it = pool.iterator();
while (it.hasNext()) {
final Searcher searcher = it.next();
try {searcher.close();} catch (IOException e) {}
it.remove();
}
} finally {
lock.unlock();
}
}
}

View File

@ -17,7 +17,7 @@ import java.io.*;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class SearchApp { public class SearcherTest {
public static void printHelp(String[] args) { public static void printHelp(String[] args) {
System.out.print("ip2region xdb searcher\n"); System.out.print("ip2region xdb searcher\n");

View File

@ -29,6 +29,7 @@ public class Searcher {
private final Version version; private final Version version;
// random access file handle for file-based search // random access file handle for file-based search
private final String xdbPath;
private final RandomAccessFile handle; private final RandomAccessFile handle;
private int ioCount = 0; private int ioCount = 0;
@ -61,6 +62,7 @@ public class Searcher {
public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException { public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException {
this.version = version; this.version = version;
this.xdbPath = dbFile;
if (cBuff != null) { if (cBuff != null) {
this.handle = null; this.handle = null;
this.vectorIndex = null; this.vectorIndex = null;
@ -173,6 +175,16 @@ public class Searcher {
} }
} }
@Override public String toString() {
return String.format(
"%s->{version:%s, xdb:%s, vIndex:%s, cBuffer:%s}",
super.toString(),
version.name, xdbPath,
vectorIndex == null ? "null" : String.valueOf(vectorIndex.length),
contentBuff == null ? "null" : String.valueOf(contentBuff.length())
);
}
// --- static util function // --- static util function
public static Header loadHeader(RandomAccessFile handle) throws IOException { public static Header loadHeader(RandomAccessFile handle) throws IOException {

View File

@ -79,4 +79,36 @@ public class Util
return 0; return 0;
} }
public static byte[] ipAddOne(byte[] ip) {
final byte[] r = new byte[ip.length];
System.arraycopy(ip, 0, r, 0, ip.length);
for (int i = ip.length - 1; i >= 0; i--) {
final int v = (int)(r[i] & 0xFF);
if (v < 255) { // No overflow
r[i]++;
break;
}
r[i] = 0;
}
return r;
}
public static byte[] ipSubOne(byte[] ip) {
final byte[] r = new byte[ip.length];
System.arraycopy(ip, 0, r, 0, ip.length);
for (int i = ip.length - 1; i >= 0; i--) {
final int v = (int)(r[i] & 0xFF);
if (v > 0) { // No borrow needed
r[i]--;
break;
}
r[i] = (byte) 0xFF; // borrow from the next byte
}
return r;
}
} }

View File

@ -0,0 +1,42 @@
package org.lionsoul.ip2region;
import java.io.IOException;
import java.security.CodeSource;
import org.junit.Test;
import org.lionsoul.ip2region.xdb.Log;
import org.lionsoul.ip2region.xdb.XdbException;
public class ConfigTest {
private static final Log log = Log.getLogger(ConfigTest.class).setLevel(Log.DEBUG);
public static final String getDataPath(String xdbFile) {
final CodeSource cs = ConfigTest.class.getProtectionDomain().getCodeSource();
if (cs != null) {
// log.debugf("code path: %s", cs.getLocation().getPath().concat("../../../../data/"));
return cs.getLocation().getPath().concat("../../../../data/").concat(xdbFile);
} else {
return "../../../../data/".concat(xdbFile);
}
}
@Test
public void testBuildV4Config() throws IOException, XdbException {
final Config v4Config = Config.custom()
.setCachePolicy(Config.BufferCache)
.setXdbPath(getDataPath("ip2region_v4.xdb"))
.setSeachers(20)
.asV4();
log.debugf("builded config: %s", v4Config);
}
@Test
public void testBuildV6Config() throws IOException, XdbException {
final Config v4Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setXdbPath(getDataPath("ip2region_v6.xdb"))
.setSeachers(20)
.asV6();
log.debugf("builded config: %s", v4Config);
}
}

View File

@ -0,0 +1,156 @@
package org.lionsoul.ip2region;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.junit.Test;
import org.lionsoul.ip2region.xdb.InetAddressException;
import org.lionsoul.ip2region.xdb.Log;
import org.lionsoul.ip2region.xdb.Util;
import org.lionsoul.ip2region.xdb.XdbException;
public class Ip2RegionTest {
private static final Log log = Log.getLogger(Ip2RegionTest.class).setLevel(Log.DEBUG);
@Test
public void TestConfigCreate() throws IOException, XdbException, InetAddressException, InterruptedException {
final Config v4Config = Config.custom()
.setCachePolicy(Config.NoCache)
.setSeachers(10)
.setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb"))
.asV4();
final Config v6Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(10)
.setXdbPath(ConfigTest.getDataPath("ip2region_v6.xdb"))
.asV6();
byte[] v4Bytes = Util.parseIP("113.92.157.29");
byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e");
final Ip2Region ip2Region = Ip2Region.create(v4Config, v6Config);
for (int i = 0; i < 50; i++) {
v4Bytes = Util.ipAddOne(v4Bytes);
v6Bytes = Util.ipAddOne(v6Bytes);
final String v4Region = ip2Region.search(v4Bytes);
final String v6Region = ip2Region.search(v6Bytes);
log.debugf("search(%s)=%s, search(%s)=%s", Util.ipToString(v4Bytes), v4Region, Util.ipToString(v6Bytes), v6Region);
}
ip2Region.close();
log.debugf("ip2region closed gracefully");
}
@Test
public void TestPathCreate() throws InetAddressException, IOException, XdbException, InterruptedException {
byte[] v4Bytes = Util.parseIP("113.92.157.29");
byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e");
final Ip2Region ip2Region = Ip2Region.create(ConfigTest.getDataPath("ip2region_v4.xdb"), ConfigTest.getDataPath("ip2region_v6.xdb"));
for (int i = 0; i < 50; i++) {
v4Bytes = Util.ipAddOne(v4Bytes);
v6Bytes = Util.ipAddOne(v6Bytes);
final String v4Region = ip2Region.search(v4Bytes);
final String v6Region = ip2Region.search(v6Bytes);
log.debugf("search(%s)=%s, search(%s)=%s", Util.ipToString(v4Bytes), v4Region, Util.ipToString(v6Bytes), v6Region);
}
ip2Region.close();
log.debugf("ip2region closed gracefully");
}
@Test
public void TestInMemSearch() throws IOException, XdbException, InetAddressException, InterruptedException {
final Config v4Config = Config.custom()
.setCachePolicy(Config.BufferCache)
.setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb"))
.asV4();
final Config v6Config = Config.custom()
.setCachePolicy(Config.BufferCache)
.setXdbPath(ConfigTest.getDataPath("ip2region_v6.xdb"))
.asV6();
byte[] v4Bytes = Util.parseIP("113.92.157.29");
byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e");
final Ip2Region ip2Region = Ip2Region.create(v4Config, v6Config);
for (int i = 0; i < 50; i++) {
v4Bytes = Util.ipAddOne(v4Bytes);
v6Bytes = Util.ipAddOne(v6Bytes);
final String v4Region = ip2Region.search(v4Bytes);
final String v6Region = ip2Region.search(v6Bytes);
log.debugf("search(%s)=%s, search(%s)=%s", Util.ipToString(v4Bytes), v4Region, Util.ipToString(v6Bytes), v6Region);
}
ip2Region.close();
log.debugf("ip2region closed gracefully");
}
@Test
public void TestConcurrentCall() throws IOException, XdbException, InetAddressException, InterruptedException {
final Config v4Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(15)
.setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb"))
.asV4();
final Config v6Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(15)
.setXdbPath(ConfigTest.getDataPath("ip2region_v6.xdb"))
.asV6();
byte[] v4Bytes = Util.parseIP("113.92.157.29");
byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e");
final int threads = 50;
final Ip2Region ip2Region = Ip2Region.create(v4Config, v6Config);
final CountDownLatch latch = new CountDownLatch(threads);
final long startTime = System.currentTimeMillis();
for (int i = 0; i < threads; i++) {
final Runnable t = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
final byte[] ipBytes = i % 2 == 0 ? v4Bytes : v6Bytes;
try {
ip2Region.search(ipBytes);
} catch (InetAddressException | IOException | InterruptedException e) {
log.errorf("failed to search(%s): %s", Util.ipToString(ipBytes), e.getMessage());
}
}
latch.countDown();
}
};
t.run();
}
latch.await();
final long costs = System.currentTimeMillis() - startTime;
log.debugf("all search finished in %dms", costs);
ip2Region.close();
log.debugf("ip2region closed gracefully");
}
@Test
public void TestV4Only() throws IOException, XdbException, InetAddressException, InterruptedException {
final Config v4Config = Config.custom()
.setCachePolicy(Config.NoCache)
.setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb"))
.asV4();
byte[] v4Bytes = Util.parseIP("113.92.157.29");
byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e");
final Ip2Region ip2Region = Ip2Region.create(v4Config, null);
for (int i = 0; i < 10; i++) {
v4Bytes = Util.ipAddOne(v4Bytes);
final String v4Region = ip2Region.search(v4Bytes);
final String v6Region = ip2Region.search(v6Bytes);
log.debugf("search(%s)=%s, search(%s)=%s", Util.ipToString(v4Bytes), v4Region, Util.ipToString(v6Bytes), v6Region);
}
ip2Region.close();
log.debugf("ip2region closed gracefully");
}
}

View File

@ -0,0 +1,58 @@
package org.lionsoul.ip2region;
import org.junit.Test;
import org.lionsoul.ip2region.xdb.Log;
import org.lionsoul.ip2region.xdb.Searcher;
public class SearcherPoolTest {
private static final Log log = Log.getLogger(SearcherPoolTest.class).setLevel(Log.DEBUG);
@Test
public void testV4SeacherPool() throws Exception {
final Config v4Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(5)
.setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb"))
.asV4();
final String ipStr = "58.250.36.41";
final SearcherPool v4Pool = SearcherPool.create(v4Config);
for (int i = 0; i < 20; i++) {
final Searcher searcher = v4Pool.borrowSearcher();
log.debugf("borrowed searcher %d: %s", i, searcher.toString());
final String region = searcher.search(ipStr);
log.debugf("search(%s)=%s", ipStr, region);
v4Pool.returnSearcher(searcher);
log.debugf("return searcher %d", i);
}
v4Pool.close();
log.debugf("v4 searcher pool closed gracefully");
}
@Test
public void testV6SeacherPool() throws Exception {
final Config v6Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(5)
.setXdbPath(ConfigTest.getDataPath("ip2region_v6.xdb"))
.asV6();
final String ipStr = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";
final SearcherPool v4Pool = SearcherPool.create(v6Config);
for (int i = 0; i < 20; i++) {
final Searcher searcher = v4Pool.borrowSearcher();
log.debugf("borrowed searcher %d: %s", i, searcher.toString());
final String region = searcher.search(ipStr);
log.debugf("search(%s)=%s", ipStr, region);
v4Pool.returnSearcher(searcher);
log.debugf("return searcher %d", i);
}
v4Pool.close();
log.debugf("v6 searcher pool closed gracefully");
}
}