Switch to deticated DNSCache interface + reference LRU implementation.
This commit is contained in:
parent
4da60e7e20
commit
8e848f7afd
|
@ -13,8 +13,6 @@ import java.security.SecureRandom;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -48,23 +46,26 @@ public class Client {
|
|||
/**
|
||||
* The internal DNS cache.
|
||||
*/
|
||||
protected LinkedHashMap<Question, DNSMessage> cache;
|
||||
protected DNSCache cache;
|
||||
|
||||
/**
|
||||
* Maximum acceptable ttl.
|
||||
* Create a new DNS client with the given DNS cache.
|
||||
* @param cache The backend DNS cache.
|
||||
*/
|
||||
protected long maxTTL = 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Create a new DNS client.
|
||||
*/
|
||||
public Client() {
|
||||
public Client(DNSCache cache) {
|
||||
try {
|
||||
random = SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e1) {
|
||||
random = new SecureRandom();
|
||||
}
|
||||
setCacheSize(10);
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DNS client.
|
||||
*/
|
||||
public Client() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,20 +133,9 @@ public class Client {
|
|||
*/
|
||||
public DNSMessage query(Question q, String host, int port) throws IOException {
|
||||
DNSMessage dnsMessage = (cache == null) ? null : cache.get(q);
|
||||
if (dnsMessage != null && dnsMessage.getReceiveTimestamp() > 0l) {
|
||||
// check the ttl
|
||||
long ttl = maxTTL;
|
||||
for (Record r : dnsMessage.getAnswers()) {
|
||||
ttl = Math.min(ttl, r.ttl);
|
||||
}
|
||||
for (Record r : dnsMessage.getAdditionalResourceRecords()) {
|
||||
ttl = Math.min(ttl, r.ttl);
|
||||
}
|
||||
if (dnsMessage.getReceiveTimestamp() + ttl <
|
||||
System.currentTimeMillis()) {
|
||||
if (dnsMessage != null) {
|
||||
return dnsMessage;
|
||||
}
|
||||
}
|
||||
DNSMessage message = new DNSMessage();
|
||||
message.setQuestions(new Question[]{q});
|
||||
message.setRecursionDesired(true);
|
||||
|
@ -326,52 +316,4 @@ public class Client {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the cache size (default 10).
|
||||
* @param maximumSize The new cache size or 0 to disable.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public void setCacheSize(final int maximumSize) {
|
||||
if (maximumSize == 0) {
|
||||
this.cache = null;
|
||||
} else {
|
||||
LinkedHashMap<Question,DNSMessage> old = cache;
|
||||
cache = new LinkedHashMap<Question,DNSMessage>() {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(
|
||||
Entry<Question, DNSMessage> eldest) {
|
||||
return size() > maximumSize;
|
||||
}
|
||||
};
|
||||
if (old != null) {
|
||||
cache.putAll(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the DNS cache.
|
||||
*/
|
||||
public void flushCache() {
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current maximum record ttl.
|
||||
* @return The maximum record ttl.
|
||||
*/
|
||||
public long getMaxTTL() {
|
||||
return maxTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum record ttl.
|
||||
* @param maxTTL The new maximum ttl.
|
||||
*/
|
||||
public void setMaxTTL(long maxTTL) {
|
||||
this.maxTTL = maxTTL;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package de.measite.minidns;
|
||||
|
||||
/**
|
||||
* Cache for DNS Entries. Implementations must be thread safe.
|
||||
*/
|
||||
public interface DNSCache {
|
||||
|
||||
void put(Question q, DNSMessage message);
|
||||
|
||||
DNSMessage get(Question q);
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package de.measite.minidns;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* LRU based DNSCache backed by a LinkedHashMap.
|
||||
*/
|
||||
public class LRUCache implements DNSCache {
|
||||
|
||||
/**
|
||||
* Internal miss count.
|
||||
*/
|
||||
protected long missCount = 0l;
|
||||
|
||||
/**
|
||||
* Internal expire count (subset of misses that was caused by expire).
|
||||
*/
|
||||
protected long expireCount = 0l;
|
||||
|
||||
/**
|
||||
* Internal hit count.
|
||||
*/
|
||||
protected long hitCount = 0l;
|
||||
|
||||
/**
|
||||
* The internal capacity of the backend cache.
|
||||
*/
|
||||
protected int capacity;
|
||||
|
||||
/**
|
||||
* The upper bound of the ttl. All longer TTLs will be capped by this ttl.
|
||||
*/
|
||||
protected long maxTTL;
|
||||
|
||||
/**
|
||||
* The backend cache.
|
||||
*/
|
||||
protected LinkedHashMap<Question, DNSMessage> backend;
|
||||
|
||||
/**
|
||||
* Create a new LRUCache with given capacity and upper bound ttl.
|
||||
* @param capacity The internal capacity.
|
||||
* @param maxTTL The upper bound for any ttl.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public LRUCache(final int capacity, final long maxTTL) {
|
||||
this.capacity = capacity;
|
||||
this.maxTTL = maxTTL;
|
||||
backend = new LinkedHashMap<Question,DNSMessage>(
|
||||
Math.min(capacity, 11), 0.75f, true)
|
||||
{
|
||||
@Override
|
||||
protected boolean removeEldestEntry(
|
||||
Entry<Question, DNSMessage> eldest) {
|
||||
return size() > capacity;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new LRUCache with given capacity.
|
||||
* @param capacity
|
||||
*/
|
||||
public LRUCache(final int capacity) {
|
||||
this(capacity, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void put(Question q, DNSMessage message) {
|
||||
if (message.getReceiveTimestamp() <= 0l) {
|
||||
return;
|
||||
}
|
||||
backend.put(q, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized DNSMessage get(Question q) {
|
||||
DNSMessage message = backend.get(q);
|
||||
if (message == null) {
|
||||
missCount++;
|
||||
return null;
|
||||
}
|
||||
|
||||
long ttl = maxTTL;
|
||||
for (Record r : message.getAnswers()) {
|
||||
ttl = Math.min(ttl, r.ttl);
|
||||
}
|
||||
for (Record r : message.getAdditionalResourceRecords()) {
|
||||
ttl = Math.min(ttl, r.ttl);
|
||||
}
|
||||
if (message.getReceiveTimestamp() + ttl > System.currentTimeMillis()) {
|
||||
missCount++;
|
||||
expireCount++;
|
||||
backend.remove(q);
|
||||
return null;
|
||||
} else {
|
||||
hitCount++;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all entries in this cache.
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
backend.clear();
|
||||
missCount = 0l;
|
||||
hitCount = 0l;
|
||||
expireCount = 0l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the miss count of this cache which is the number of fruitless
|
||||
* get calls since this cache was last resetted.
|
||||
* @return The number of cache misses.
|
||||
*/
|
||||
public long getMissCount() {
|
||||
return missCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of expires (cache hits that have had a ttl to low to be
|
||||
* retrieved).
|
||||
* @return The expire count.
|
||||
*/
|
||||
public long getExpireCount() {
|
||||
return expireCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache hit count (all sucessful calls to get).
|
||||
* @return The hit count.
|
||||
*/
|
||||
public long getHitCount() {
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue