searcher pool and tests
This commit is contained in:
parent
a8087e06b7
commit
f8a154aeda
|
|
@ -26,16 +26,16 @@ public class Ip2Region {
|
||||||
/* v6 pool */
|
/* v6 pool */
|
||||||
private final SearcherPool v6Pool;
|
private final SearcherPool v6Pool;
|
||||||
|
|
||||||
public Ip2Region(Config v4Config, Config v6Config) {
|
public Ip2Region(Config v4Config, Config v6Config) throws IOException {
|
||||||
this.v4Pool = new SearcherPool(v4Config);
|
this.v4Pool = new SearcherPool(v4Config);
|
||||||
this.v6Pool = new SearcherPool(v6Config);
|
this.v6Pool = new SearcherPool(v6Config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String search(String ipString) throws InetAddressException, IOException {
|
public String search(String ipString) throws InetAddressException, IOException, InterruptedException {
|
||||||
return search(Util.parseIP(ipString));
|
return search(Util.parseIP(ipString));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String search(byte[] ipBytes) throws InetAddressException, IOException {
|
public String search(byte[] ipBytes) throws InetAddressException, IOException, InterruptedException {
|
||||||
// 1, define the pool with the input ip
|
// 1, define the pool with the input ip
|
||||||
final SearcherPool pool;
|
final SearcherPool pool;
|
||||||
if (ipBytes.length == 4) {
|
if (ipBytes.length == 4) {
|
||||||
|
|
@ -47,21 +47,13 @@ public class Ip2Region {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2, get a searcher from the pool
|
// 2, get a searcher from the pool
|
||||||
final Searcher searcher = pool.getSearcher();
|
final Searcher searcher = pool.borrowSearcher();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 3, do the search
|
// 3, do the search
|
||||||
final String region = searcher.search(ipBytes);
|
return searcher.search(ipBytes);
|
||||||
return region;
|
} finally {
|
||||||
} catch (InetAddressException e) {
|
pool.returnSearcher(searcher);
|
||||||
// for the inet address error and we should return the searcher
|
|
||||||
throw e;
|
|
||||||
} catch (IOException e) {
|
|
||||||
// for the IOException usually means something is wrong with the read operation to the xdb file
|
|
||||||
// and we choose to keep the searcher and destory it right now
|
|
||||||
// so we will create a new one for the next search
|
|
||||||
try {searcher.close();} catch (IOException e1) {}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
package org.lionsoul.ip2region;
|
package org.lionsoul.ip2region;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.locks.Condition;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
|
|
@ -19,43 +20,94 @@ import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
* Date 2025/11/21
|
* Date 2025/11/21
|
||||||
*/
|
*/
|
||||||
public class SearcherPool {
|
public class SearcherPool {
|
||||||
|
public static final int WAIT_OVERLOADED = 1;
|
||||||
|
public static final int NEW_OVERLOADED = 2;
|
||||||
|
|
||||||
// config instance
|
// config instance
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
|
||||||
// searcher pool
|
// searcher pool
|
||||||
private final Queue<Searcher> pool;
|
private final Queue<Searcher> pool;
|
||||||
|
|
||||||
// searcher lock
|
// lock & conditions
|
||||||
private final ReentrantLock lock;
|
private final ReentrantLock lock;
|
||||||
|
private final Condition emptyCondition;
|
||||||
|
private final Condition fullCondition;
|
||||||
|
|
||||||
// searcher numbers that was loaned out
|
// searcher numbers that was loaned out
|
||||||
private final AtomicInteger loanCount;
|
private int loanCount;
|
||||||
|
|
||||||
public SearcherPool(Config config) {
|
public SearcherPool(Config config) throws IOException {
|
||||||
this(config, false);
|
this(config, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearcherPool(Config config, boolean fair) {
|
public SearcherPool(Config config, boolean fair) throws IOException {
|
||||||
|
assert config.searchers > 0;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.pool = new LinkedList<>();
|
this.pool = new LinkedList<>();
|
||||||
this.lock = new ReentrantLock(fair);
|
this.lock = new ReentrantLock(fair);
|
||||||
this.loanCount = new AtomicInteger(0);
|
this.emptyCondition = this.lock.newCondition();
|
||||||
}
|
this.fullCondition = this.lock.newCondition();
|
||||||
|
this.loanCount = 0;
|
||||||
|
|
||||||
// init the searcher pool
|
// create the searchers
|
||||||
public void init() throws IOException {
|
|
||||||
for (int i = 0; i < config.searchers; i++) {
|
for (int i = 0; i < config.searchers; i++) {
|
||||||
final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer);
|
final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer);
|
||||||
pool.add(searcher);
|
pool.add(searcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Searcher getSearcher() {
|
public Config getConfig() {
|
||||||
return null;
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// close the searcher pool
|
||||||
public void close() {
|
public void close() throws InterruptedException {
|
||||||
this.loanCount.set(config.searchers);
|
lock.lock();
|
||||||
|
try {
|
||||||
|
while (loanCount > 0) {
|
||||||
|
fullCondition.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testV4SeacherPool() throws Exception {
|
||||||
|
final Config v4Config = Config.custom()
|
||||||
|
.setCachePolicy(Config.VIndexCache)
|
||||||
|
.setSeachers(5)
|
||||||
|
.setXdbPath(getDataPath("ip2region_v4.xdb"))
|
||||||
|
.asV4();
|
||||||
|
|
||||||
|
|
||||||
|
final String ipStr = "58.250.36.41";
|
||||||
|
final SearcherPool v4Pool = new SearcherPool(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(getDataPath("ip2region_v6.xdb"))
|
||||||
|
.asV6();
|
||||||
|
|
||||||
|
|
||||||
|
final String ipStr = "240e:3b7:3272:d8d0:db09:c067:8d59:539e";
|
||||||
|
final SearcherPool v4Pool = new SearcherPool(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");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue