searcher pool and tests
This commit is contained in:
parent
a8087e06b7
commit
f8a154aeda
|
|
@ -26,16 +26,16 @@ public class Ip2Region {
|
|||
/* v6 pool */
|
||||
private final SearcherPool v6Pool;
|
||||
|
||||
public Ip2Region(Config v4Config, Config v6Config) {
|
||||
public Ip2Region(Config v4Config, Config v6Config) throws IOException {
|
||||
this.v4Pool = new SearcherPool(v4Config);
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
final SearcherPool pool;
|
||||
if (ipBytes.length == 4) {
|
||||
|
|
@ -47,21 +47,13 @@ public class Ip2Region {
|
|||
}
|
||||
|
||||
// 2, get a searcher from the pool
|
||||
final Searcher searcher = pool.getSearcher();
|
||||
final Searcher searcher = pool.borrowSearcher();
|
||||
|
||||
try {
|
||||
// 3, do the search
|
||||
final String region = searcher.search(ipBytes);
|
||||
return region;
|
||||
} catch (InetAddressException e) {
|
||||
// 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;
|
||||
return searcher.search(ipBytes);
|
||||
} finally {
|
||||
pool.returnSearcher(searcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@
|
|||
package org.lionsoul.ip2region;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.lionsoul.ip2region.xdb.Searcher;
|
||||
|
|
@ -19,43 +20,94 @@ import org.lionsoul.ip2region.xdb.Searcher;
|
|||
* Date 2025/11/21
|
||||
*/
|
||||
public class SearcherPool {
|
||||
public static final int WAIT_OVERLOADED = 1;
|
||||
public static final int NEW_OVERLOADED = 2;
|
||||
|
||||
// config instance
|
||||
private final Config config;
|
||||
|
||||
// searcher pool
|
||||
private final Queue<Searcher> pool;
|
||||
|
||||
// searcher lock
|
||||
// lock & conditions
|
||||
private final ReentrantLock lock;
|
||||
private final Condition emptyCondition;
|
||||
private final Condition fullCondition;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
public SearcherPool(Config config, boolean fair) {
|
||||
public SearcherPool(Config config, boolean fair) throws IOException {
|
||||
assert config.searchers > 0;
|
||||
this.config = config;
|
||||
this.pool = new LinkedList<>();
|
||||
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
|
||||
public void init() throws IOException {
|
||||
// create the searchers
|
||||
for (int i = 0; i < config.searchers; i++) {
|
||||
final Searcher searcher = new Searcher(config.ipVersion, config.xdbPath, config.vIndex, config.cBuffer);
|
||||
pool.add(searcher);
|
||||
}
|
||||
}
|
||||
|
||||
public Searcher getSearcher() {
|
||||
return null;
|
||||
public Config getConfig() {
|
||||
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
|
||||
public void close() {
|
||||
this.loanCount.set(config.searchers);
|
||||
public void close() throws InterruptedException {
|
||||
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;
|
||||
|
||||
// random access file handle for file-based search
|
||||
private final String xdbPath;
|
||||
private final RandomAccessFile handle;
|
||||
|
||||
private int ioCount = 0;
|
||||
|
|
@ -61,6 +62,7 @@ public class Searcher {
|
|||
|
||||
public Searcher(Version version, String dbFile, byte[] vectorIndex, LongByteArray cBuff) throws IOException {
|
||||
this.version = version;
|
||||
this.xdbPath = dbFile;
|
||||
if (cBuff != null) {
|
||||
this.handle = 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
|
||||
|
||||
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