ip2region service with thread safe and auto ip version detect supports

This commit is contained in:
lionsoul2014 2025-11-28 16:34:48 +08:00
parent efbbb576ee
commit b5ae94b36d
5 changed files with 166 additions and 30 deletions

View File

@ -9,6 +9,7 @@ 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:
@ -20,15 +21,66 @@ import org.lionsoul.ip2region.xdb.Util;
*/
public class Ip2Region {
/* v4 pool */
/* v4 pool for cache policy vIndex or NoCache */
private final SearcherPool v4Pool;
/* v6 pool */
/* 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;
/**
* 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
*/
public 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
*/
public Ip2Region(Config v4Config, Config v6Config) throws IOException {
this.v4Pool = new SearcherPool(v4Config);
this.v6Pool = new SearcherPool(v6Config);
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);
}
}
public String search(String ipString) throws InetAddressException, IOException, InterruptedException {
@ -36,24 +88,62 @@ public class Ip2Region {
}
public String search(byte[] ipBytes) throws InetAddressException, IOException, InterruptedException {
// 1, define the pool with the input ip
final SearcherPool pool;
if (ipBytes.length == 4) {
pool = v4Pool;
return v4Search(ipBytes);
} else if (ipBytes.length == 16) {
pool = v6Pool;
return v6Search(ipBytes);
} else {
throw new InetAddressException("invalid byte ip address with length=" + ipBytes.length);
}
}
// 2, get a searcher from the pool
final Searcher searcher = pool.borrowSearcher();
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 {
// 3, do the search
return searcher.search(ipBytes);
} finally {
pool.returnSearcher(searcher);
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

@ -94,10 +94,14 @@ public class SearcherPool {
// 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();
fullCondition.wait(timeoutMillis);
}
final Iterator<Searcher> it = pool.iterator();

View File

@ -10,8 +10,8 @@ public class ConfigTest {
private static final Log log = Log.getLogger(ConfigTest.class).setLevel(Log.DEBUG);
public String getDataPath(String xdbFile) {
final CodeSource cs = this.getClass().getProtectionDomain().getCodeSource();
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);

View File

@ -0,0 +1,54 @@
package org.lionsoul.ip2region;
import java.io.IOException;
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("1.2.3.4");
byte[] v6Bytes = Util.parseIP("240e:3b7:3272:d8d0:db09:c067:8d59:539e");
final Ip2Region ip2Region = new Ip2Region(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() {
}
@Test
public void TestInMemSearch() {
}
}

View File

@ -1,31 +1,19 @@
package org.lionsoul.ip2region;
import java.security.CodeSource;
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(ConfigTest.class).setLevel(Log.DEBUG);
public String getDataPath(String xdbFile) {
final CodeSource cs = this.getClass().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);
}
}
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(getDataPath("ip2region_v4.xdb"))
.setXdbPath(ConfigTest.getDataPath("ip2region_v4.xdb"))
.asV4();
@ -49,7 +37,7 @@ public class SearcherPoolTest {
final Config v6Config = Config.custom()
.setCachePolicy(Config.VIndexCache)
.setSeachers(5)
.setXdbPath(getDataPath("ip2region_v6.xdb"))
.setXdbPath(ConfigTest.getDataPath("ip2region_v6.xdb"))
.asV6();