HTTPCLIENT-975: added CacheConfig settings to provide more detailed
control over the background revalidation thread pool, and enabled the stale-while-revalidate feature by default with a single worker thread and a max queue size of 100 pending revalidations, which should be a pretty safe setting. git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1050360 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d012c1041c
commit
83efe306fd
|
@ -29,10 +29,12 @@ package org.apache.http.impl.client.cache;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -59,8 +61,14 @@ public class AsynchronousValidator {
|
||||||
* @param cachingClient
|
* @param cachingClient
|
||||||
* @param numThreads
|
* @param numThreads
|
||||||
*/
|
*/
|
||||||
public AsynchronousValidator(CachingHttpClient cachingClient, int numThreads) {
|
public AsynchronousValidator(CachingHttpClient cachingClient, CacheConfig config) {
|
||||||
this(cachingClient, Executors.newFixedThreadPool(numThreads));
|
this(cachingClient,
|
||||||
|
new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
|
||||||
|
config.getAsynchronousWorkersMax(),
|
||||||
|
(long)config.getAsynchronousWorkerIdleLifetimeSecs(),
|
||||||
|
TimeUnit.SECONDS,
|
||||||
|
new ArrayBlockingQueue<Runnable>(config.getRevalidationQueueSize()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -51,22 +51,35 @@ public class CacheConfig {
|
||||||
*/
|
*/
|
||||||
public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
|
public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;
|
||||||
|
|
||||||
/** Default coefficient used to heuristically determine freshness lifetime from
|
/** Default coefficient used to heuristically determine freshness
|
||||||
* cache entry.
|
* lifetime from the Last-Modified time of a cache entry.
|
||||||
*/
|
*/
|
||||||
public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;
|
public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;
|
||||||
|
|
||||||
/** Default lifetime in seconds to be assumed when we cannot calculate freshness
|
/** Default lifetime in seconds to be assumed when we cannot calculate
|
||||||
* heuristically
|
* freshness heuristically.
|
||||||
*/
|
*/
|
||||||
public final static long DEFAULT_HEURISTIC_LIFETIME = 0;
|
public final static long DEFAULT_HEURISTIC_LIFETIME = 0;
|
||||||
|
|
||||||
/** Default number of worker threads to allow for background revalidations
|
/** Default number of worker threads to allow for background revalidations
|
||||||
* resulting from the stale-while-revalidate directive; 0 disables handling
|
* resulting from the stale-while-revalidate directive.
|
||||||
* asynchronous revalidations.
|
|
||||||
*/
|
*/
|
||||||
private static final int DEFAULT_STALE_WHILE_REVALIDATE_WORKERS = 0;
|
private static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1;
|
||||||
|
|
||||||
|
/** Default minimum number of worker threads to allow for background
|
||||||
|
* revalidations resulting from the stale-while-revalidate directive.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;
|
||||||
|
|
||||||
|
/** Default maximum idle lifetime for a background revalidation thread
|
||||||
|
* before it gets reclaimed.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;
|
||||||
|
|
||||||
|
/** Default maximum queue length for background revalidation requests.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;
|
||||||
|
|
||||||
private int maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES;
|
private int maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES;
|
||||||
private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
|
private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
|
||||||
private int maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
|
private int maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
|
||||||
|
@ -74,7 +87,10 @@ public class CacheConfig {
|
||||||
private float heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
|
private float heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
|
||||||
private long heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
|
private long heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
|
||||||
private boolean isSharedCache = true;
|
private boolean isSharedCache = true;
|
||||||
private int staleWhileRevalidateWorkers = DEFAULT_STALE_WHILE_REVALIDATE_WORKERS;
|
private int asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
|
||||||
|
private int asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
|
||||||
|
private int asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
|
||||||
|
private int revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current maximum object size that will be cached.
|
* Returns the current maximum object size that will be cached.
|
||||||
|
@ -182,20 +198,76 @@ public class CacheConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set number of worker threads to allow for background revalidations resulting from,
|
* Returns the maximum number of threads to allow for background
|
||||||
* the stale-while-revalidate directive, 0 disables handling of directive
|
* revalidations due to the stale-while-revalidate directive. A
|
||||||
* @return
|
* value of 0 means background revalidations are disabled.
|
||||||
*/
|
*/
|
||||||
public int getStaleWhileRevalidateWorkers() {
|
public int getAsynchronousWorkersMax() {
|
||||||
return staleWhileRevalidateWorkers;
|
return asynchronousWorkersMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get number of worker threads to allow for background revalidations resulting from,
|
* Sets the maximum number of threads to allow for background
|
||||||
* the stale-while-revalidate directive, 0 disables handling of directive
|
* revalidations due to the stale-while-revalidate directive.
|
||||||
|
* @param max number of threads; a value of 0 disables background
|
||||||
|
* revalidations.
|
||||||
*/
|
*/
|
||||||
public void setStaleWhileRevalidateWorkers(int staleWhileRevalidateWorkers) {
|
public void setAsynchronousWorkersMax(int max) {
|
||||||
this.staleWhileRevalidateWorkers = staleWhileRevalidateWorkers;
|
this.asynchronousWorkersMax = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum number of threads to keep alive for background
|
||||||
|
* revalidations due to the stale-while-revalidate directive.
|
||||||
|
*/
|
||||||
|
public int getAsynchronousWorkersCore() {
|
||||||
|
return asynchronousWorkersCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum number of threads to keep alive for background
|
||||||
|
* revalidations due to the stale-while-revalidate directive.
|
||||||
|
* @param min should be greater than zero and less than or equal
|
||||||
|
* to <code>getAsynchronousWorkersMax()</code>
|
||||||
|
*/
|
||||||
|
public void setAsynchronousWorkersCore(int min) {
|
||||||
|
this.asynchronousWorkersCore = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current maximum idle lifetime in seconds for a
|
||||||
|
* background revalidation worker thread. If a worker thread is idle
|
||||||
|
* for this long, and there are more than the core number of worker
|
||||||
|
* threads alive, the worker will be reclaimed.
|
||||||
|
*/
|
||||||
|
public int getAsynchronousWorkerIdleLifetimeSecs() {
|
||||||
|
return asynchronousWorkerIdleLifetimeSecs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current maximum idle lifetime in seconds for a
|
||||||
|
* background revalidation worker thread. If a worker thread is idle
|
||||||
|
* for this long, and there are more than the core number of worker
|
||||||
|
* threads alive, the worker will be reclaimed.
|
||||||
|
* @param secs idle lifetime in seconds
|
||||||
|
*/
|
||||||
|
public void setAsynchronousWorkerIdleLifetimeSecs(int secs) {
|
||||||
|
this.asynchronousWorkerIdleLifetimeSecs = secs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current maximum queue size for background revalidations.
|
||||||
|
*/
|
||||||
|
public int getRevalidationQueueSize() {
|
||||||
|
return revalidationQueueSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current maximum queue size for background revalidations.
|
||||||
|
*/
|
||||||
|
public void setRevalidationQueueSize(int size) {
|
||||||
|
this.revalidationQueueSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class CachingHttpClient implements HttpClient {
|
||||||
this.responseCompliance = new ResponseProtocolCompliance();
|
this.responseCompliance = new ResponseProtocolCompliance();
|
||||||
this.requestCompliance = new RequestProtocolCompliance();
|
this.requestCompliance = new RequestProtocolCompliance();
|
||||||
|
|
||||||
this.asynchRevalidator = makeAsynchronousValidator(config.getStaleWhileRevalidateWorkers());
|
this.asynchRevalidator = makeAsynchronousValidator(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachingHttpClient() {
|
public CachingHttpClient() {
|
||||||
|
@ -197,13 +197,13 @@ public class CachingHttpClient implements HttpClient {
|
||||||
this.conditionalRequestBuilder = conditionalRequestBuilder;
|
this.conditionalRequestBuilder = conditionalRequestBuilder;
|
||||||
this.responseCompliance = responseCompliance;
|
this.responseCompliance = responseCompliance;
|
||||||
this.requestCompliance = requestCompliance;
|
this.requestCompliance = requestCompliance;
|
||||||
this.asynchRevalidator = makeAsynchronousValidator(config.getStaleWhileRevalidateWorkers());
|
this.asynchRevalidator = makeAsynchronousValidator(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AsynchronousValidator makeAsynchronousValidator(
|
private AsynchronousValidator makeAsynchronousValidator(
|
||||||
int numWorkers) {
|
CacheConfig config) {
|
||||||
if (numWorkers > 0) {
|
if (config.getAsynchronousWorkersMax() > 0) {
|
||||||
return new AsynchronousValidator(this, numWorkers);
|
return new AsynchronousValidator(this, config);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,10 @@ public class TestAsynchronousValidator {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRevalidateCacheEntryEndToEnd() throws ProtocolException, IOException, InterruptedException {
|
public void testRevalidateCacheEntryEndToEnd() throws ProtocolException, IOException, InterruptedException {
|
||||||
impl = new AsynchronousValidator(mockClient, 1);
|
CacheConfig config = new CacheConfig();
|
||||||
|
config.setAsynchronousWorkersMax(1);
|
||||||
|
config.setAsynchronousWorkersCore(1);
|
||||||
|
impl = new AsynchronousValidator(mockClient, config);
|
||||||
|
|
||||||
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
|
||||||
EasyMock.expect(mockClient.revalidateCacheEntry(target, request, mockContext, mockCacheEntry)).andReturn(null);
|
EasyMock.expect(mockClient.revalidateCacheEntry(target, request, mockContext, mockCacheEntry)).andReturn(null);
|
||||||
|
|
|
@ -62,7 +62,6 @@ import org.easymock.Capture;
|
||||||
import org.easymock.classextension.EasyMock;
|
import org.easymock.classextension.EasyMock;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class TestCachingHttpClient {
|
public class TestCachingHttpClient {
|
||||||
|
@ -341,9 +340,6 @@ public class TestCachingHttpClient {
|
||||||
Assert.assertEquals(0, impl.getCacheUpdates());
|
Assert.assertEquals(0, impl.getCacheUpdates());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: re-enable when background validation enabled by default, or adjust
|
|
||||||
// test to specify background validation in CacheConfig
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
|
public void testUnsuitableValidatableCacheEntryCausesRevalidation() throws Exception {
|
||||||
mockImplMethods(REVALIDATE_CACHE_ENTRY);
|
mockImplMethods(REVALIDATE_CACHE_ENTRY);
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
|
||||||
public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
|
public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
params.setStaleWhileRevalidateWorkers(1);
|
params.setAsynchronousWorkersMax(1);
|
||||||
impl = new CachingHttpClient(mockBackend, cache, params);
|
impl = new CachingHttpClient(mockBackend, cache, params);
|
||||||
|
|
||||||
HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
|
HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
|
||||||
|
|
Loading…
Reference in New Issue