9.4.x->10.0.x
This commit is contained in:
commit
90f15f278b
|
@ -65,7 +65,9 @@ public class HttpAuthenticationStore implements AuthenticationStore
|
||||||
@Override
|
@Override
|
||||||
public void addAuthenticationResult(Authentication.Result result)
|
public void addAuthenticationResult(Authentication.Result result)
|
||||||
{
|
{
|
||||||
results.put(result.getURI(), result);
|
URI uri = result.getURI();
|
||||||
|
if (uri != null)
|
||||||
|
results.put(uri, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -426,16 +426,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
|
||||||
|
|
||||||
if (getHttpExchanges().isEmpty())
|
if (getHttpExchanges().isEmpty())
|
||||||
{
|
{
|
||||||
if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty())
|
tryRemoveIdleDestination();
|
||||||
{
|
|
||||||
// There is a race condition between this thread removing the destination
|
|
||||||
// and another thread queueing a request to this same destination.
|
|
||||||
// If this destination is removed, but the request queued, a new connection
|
|
||||||
// will be opened, the exchange will be executed and eventually the connection
|
|
||||||
// will idle timeout and be closed. Meanwhile a new destination will be created
|
|
||||||
// in HttpClient and will be used for other requests.
|
|
||||||
getHttpClient().removeDestination(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -460,6 +451,22 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
|
||||||
// The call to Request.abort() will remove the exchange from the exchanges queue.
|
// The call to Request.abort() will remove the exchange from the exchanges queue.
|
||||||
for (HttpExchange exchange : new ArrayList<>(exchanges))
|
for (HttpExchange exchange : new ArrayList<>(exchanges))
|
||||||
exchange.getRequest().abort(cause);
|
exchange.getRequest().abort(cause);
|
||||||
|
if (exchanges.isEmpty())
|
||||||
|
tryRemoveIdleDestination();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryRemoveIdleDestination()
|
||||||
|
{
|
||||||
|
if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty())
|
||||||
|
{
|
||||||
|
// There is a race condition between this thread removing the destination
|
||||||
|
// and another thread queueing a request to this same destination.
|
||||||
|
// If this destination is removed, but the request queued, a new connection
|
||||||
|
// will be opened, the exchange will be executed and eventually the connection
|
||||||
|
// will idle timeout and be closed. Meanwhile a new destination will be created
|
||||||
|
// in HttpClient and will be used for other requests.
|
||||||
|
getHttpClient().removeDestination(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.client.Origin;
|
||||||
import org.eclipse.jetty.client.api.Connection;
|
import org.eclipse.jetty.client.api.Connection;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Destination;
|
import org.eclipse.jetty.client.api.Destination;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
@ -268,6 +269,33 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
|
||||||
Assert.assertNotSame(destinationBefore, destinationAfter);
|
Assert.assertNotSame(destinationBefore, destinationAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDestinationIsRemovedAfterConnectionError() throws Exception
|
||||||
|
{
|
||||||
|
String host = "localhost";
|
||||||
|
int port = connector.getLocalPort();
|
||||||
|
client.setRemoveIdleDestinations(true);
|
||||||
|
Assert.assertTrue("Destinations of a fresh client must be empty", client.getDestinations().isEmpty());
|
||||||
|
|
||||||
|
server.stop();
|
||||||
|
Request request = client.newRequest(host, port).scheme(this.scheme);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
request.send();
|
||||||
|
Assert.fail("Request to a closed port must fail");
|
||||||
|
}
|
||||||
|
catch (Exception expected)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(1);
|
||||||
|
while (!client.getDestinations().isEmpty() && System.nanoTime() < deadline)
|
||||||
|
{
|
||||||
|
Thread.sleep(10);
|
||||||
|
}
|
||||||
|
Assert.assertTrue("Destination must be removed after connection error", client.getDestinations().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
private Connection pollIdleConnection(DuplexConnectionPool connectionPool, long time, TimeUnit unit) throws InterruptedException
|
private Connection pollIdleConnection(DuplexConnectionPool connectionPool, long time, TimeUnit unit) throws InterruptedException
|
||||||
{
|
{
|
||||||
return await(() -> connectionPool.getIdleConnections().poll(), time, unit);
|
return await(() -> connectionPool.getIdleConnections().poll(), time, unit);
|
||||||
|
|
|
@ -40,18 +40,37 @@ See the `DefaultServlet` link:{JDURL}/org/eclipse/jetty/servlet/DefaultServlet.h
|
||||||
Jetty supports the following `initParameters`:
|
Jetty supports the following `initParameters`:
|
||||||
|
|
||||||
acceptRanges::
|
acceptRanges::
|
||||||
If true, range requests and responses are supported.
|
If `true`, range requests and responses are supported.
|
||||||
dirAllowed::
|
dirAllowed::
|
||||||
If true, directory listings are returned if no welcome file is found.
|
If `true`, directory listings are returned if no welcome file is found.
|
||||||
Otherwise 403 Forbidden displays.
|
Otherwise 403 Forbidden displays.
|
||||||
redirectWelcome::
|
redirectWelcome::
|
||||||
If true, welcome files are redirected rather that forwarded.
|
If `true`, welcome files are redirected rather that forwarded.
|
||||||
|
welcomeServlets::
|
||||||
|
If `true`, attempt to dispatch to welcome files that are servlets, but only after no matching static
|
||||||
|
resources could be found. If `false`, then a welcome file must exist on disk. If `exact`, then exact
|
||||||
|
servlet matches are supported without an existing file. Default is `true`. This must be `false` if you want directory listings,
|
||||||
|
but have index.jsp in your welcome file list.
|
||||||
|
precompressed::
|
||||||
|
If set to a comma separated list of encoding types (that may be listed in a requests Accept-Encoding header) to file extension mappings to look for and serve.
|
||||||
|
For example: `br=.br,gzip=.gz,bzip2=.bz`.
|
||||||
|
If set to a boolean `true`, then a default set of compressed formats will be used, otherwise no precompressed formats supported.
|
||||||
gzip::
|
gzip::
|
||||||
If set to true, then static content is served as gzip content encoded if a matching resource is found ending with ".gz".
|
Deprecated. Use `precompressed` instead. If set to `true`, then static content is served as gzip content encoded if a matching resource is found ending with ".gz".
|
||||||
resourceBase::
|
resourceBase::
|
||||||
Set to replace the context resource base.
|
Set to replace the context resource base.
|
||||||
aliases::
|
resourceCache::
|
||||||
If true, aliases of resources are allowed (that is, symbolic links and caps variations) and may bypass security constraints.
|
If set, this is a context attribute name, which the servlet will use to look for a shared ResourceCache instance.
|
||||||
|
relativeResourceBase::
|
||||||
|
Set with a pathname relative to the base of the servlet context root. Useful for only serving static content out of only specific subdirectories.
|
||||||
|
cacheControl::
|
||||||
|
If set, all static content will have this value set as the cache-control header.
|
||||||
|
pathInfoOnly::
|
||||||
|
If `true`, only the path info will be applied to the resourceBase
|
||||||
|
stylesheet::
|
||||||
|
Set with the location of an optional stylesheet that will be used to decorate the directory listing html.
|
||||||
|
etags::
|
||||||
|
If `true`, weak etags will be generated and handled.
|
||||||
maxCacheSize::
|
maxCacheSize::
|
||||||
Maximum total size of the cache or 0 for no cache.
|
Maximum total size of the cache or 0 for no cache.
|
||||||
maxCachedFileSize::
|
maxCachedFileSize::
|
||||||
|
@ -59,9 +78,11 @@ Maximum size of a file to cache.
|
||||||
maxCachedFiles::
|
maxCachedFiles::
|
||||||
Maximum number of files to cache.
|
Maximum number of files to cache.
|
||||||
useFileMappedBuffer::
|
useFileMappedBuffer::
|
||||||
If set to true, mapped file buffer serves static content.
|
If set to `true`, mapped file buffer serves static content.
|
||||||
Setting this value to false means that a direct buffer is used instead of a mapped file buffer.
|
Setting this value to `false` means that a direct buffer is used instead of a mapped file buffer.
|
||||||
By default, this is set to true.
|
By default, this is set to `true`.
|
||||||
otherGzipFileExtensions::
|
otherGzipFileExtensions::
|
||||||
A comma separated list of other file extensions that signify that a file is gzip compressed.
|
A comma separated list of other file extensions that signify that a file is gzip compressed.
|
||||||
If you don't explicitly set this, it defaults to ".svgz".
|
If you don't explicitly set this, it defaults to `.svgz`.
|
||||||
|
encodingHeaderCacheSize::
|
||||||
|
Max entries in a cache of ACCEPT-ENCODING headers
|
||||||
|
|
|
@ -34,13 +34,13 @@ It fixes many of the bugs in commonly available compression filters: it works wi
|
||||||
It has been tested with Jetty continuations and suspending requests.
|
It has been tested with Jetty continuations and suspending requests.
|
||||||
Some user-agents might be excluded from compression to avoid common browser bugs (yes, this means IE!).
|
Some user-agents might be excluded from compression to avoid common browser bugs (yes, this means IE!).
|
||||||
|
|
||||||
The `GzipHandler` is added to the entire server by the `etc/jetty-gzip.xml` file from the `gzip.mod` module.
|
The `GzipHandler` can be added to the entire server by enabling the `gzip.mod` module.
|
||||||
It may also be added to individual contexts in a context xml file.
|
It may also be added to individual contexts in a context xml file.
|
||||||
|
|
||||||
____
|
____
|
||||||
[NOTE]
|
[NOTE]
|
||||||
Jetty 9 only compresses using GZip.
|
Jetty 9 only compresses using GZip.
|
||||||
Using deflate http compression is not supported and will not function.
|
Using deflate HTTP compression is not supported and will not function.
|
||||||
____
|
____
|
||||||
|
|
||||||
[[gzip-filter-rules]]
|
[[gzip-filter-rules]]
|
||||||
|
@ -59,16 +59,25 @@ ____
|
||||||
|
|
||||||
Compressing the content can greatly improve the network bandwidth usage, but at the cost of memory and CPU cycles.
|
Compressing the content can greatly improve the network bandwidth usage, but at the cost of memory and CPU cycles.
|
||||||
The link:#default-servlet[DefaultServlet] is capable of serving pre-compressed static content, which saves memory and CPU.
|
The link:#default-servlet[DefaultServlet] is capable of serving pre-compressed static content, which saves memory and CPU.
|
||||||
By default, the `GzipHandler` will check to see if pre-compressed content exists, and pass the request through to be handled by the `DefaultServlet`.
|
|
||||||
|
The `GzipHandler` installs an output interceptor which passes through to the `DefaultServlet`.
|
||||||
|
If the content served by `DefaultServlet` is already compressed, the `GzipHandler` does nothing; if it is not compressed, the content is compressed on-the-fly.
|
||||||
|
|
||||||
|
____
|
||||||
|
[NOTE]
|
||||||
|
Automatic precompression by the `DefaultServlet` can be configured.
|
||||||
|
Read more about the `DefaultServlet` link:#default-servlet[here.]
|
||||||
|
____
|
||||||
|
|
||||||
|
|
||||||
[[gzip-filter-init]]
|
[[gzip-filter-init]]
|
||||||
==== Gzip Configuration
|
==== Gzip Configuration
|
||||||
|
|
||||||
minGzipSize::
|
minGzipSize::
|
||||||
Content will only be compressed if content length is either unknown or greater than `minGzipSize`.
|
Content will only be compressed if content length is either unknown or greater than `minGzipSize`.
|
||||||
checkGzExists::
|
checkGzExists (Deprecated)::
|
||||||
True by default.
|
False by default.
|
||||||
If set to false, the handler will not check for pre-compressed content.
|
If set to true, the handler will check for pre-compressed content.
|
||||||
includedMethods::
|
includedMethods::
|
||||||
List of HTTP methods to compress.
|
List of HTTP methods to compress.
|
||||||
If not set, only `GET` requests are compressed.
|
If not set, only `GET` requests are compressed.
|
||||||
|
|
|
@ -8,15 +8,15 @@
|
||||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||||
<Call name="addBean">
|
<Call name="addBean">
|
||||||
<Arg>
|
<Arg>
|
||||||
<New class="org.eclipse.jetty.server.LowResourceMonitor">
|
<New id="lowResourceMonitor" class="org.eclipse.jetty.server.LowResourceMonitor">
|
||||||
<Arg name="server"><Ref refid='Server'/></Arg>
|
<Arg name="server"><Ref refid='Server'/></Arg>
|
||||||
<Set name="period"><Property name="jetty.lowresources.period" default="1000"/></Set>
|
<Set name="period"><Property name="jetty.lowresources.period" deprecated="lowresources.period" default="1000"/></Set>
|
||||||
<Set name="lowResourcesIdleTimeout"><Property name="jetty.lowresources.idleTimeout" default="1000"/></Set>
|
<Set name="lowResourcesIdleTimeout"><Property name="jetty.lowresources.idleTimeout" deprecated="lowresources.lowResourcesIdleTimeout" default="1000"/></Set>
|
||||||
<Set name="monitorThreads"><Property name="jetty.lowresources.monitorThreads" default="true"/></Set>
|
<Set name="monitorThreads"><Property name="jetty.lowresources.monitorThreads" deprecated="lowresources.monitorThreads" default="true"/></Set>
|
||||||
<Set name="maxConnections"><Property name="jetty.lowresources.maxConnections" default="0"/></Set>
|
<Set name="maxConnections"><Property name="jetty.lowresources.maxConnections" deprecated="lowresources.maxConnections" default="0"/></Set>
|
||||||
<Set name="maxMemory"><Property name="jetty.lowresources.maxMemory" default="0"/></Set>
|
<Set name="maxMemory"><Property name="jetty.lowresources.maxMemory" deprecated="lowresources.maxMemory" default="0"/></Set>
|
||||||
<Set name="maxLowResourcesTime"><Property name="jetty.lowresources.maxLowResourcesTime" default="5000"/></Set>
|
<Set name="maxLowResourcesTime"><Property name="jetty.lowresources.maxLowResourcesTime" deprecated="lowresources.maxLowResourcesTime" default="5000"/></Set>
|
||||||
<Set name="acceptingInLowResources"><Property name="jetty.lowresources.accepting" default="true"/></Set>
|
<Set name="acceptingInLowResources"><Property name="jetty.lowresources.accepting" default="true"/></Set>
|
||||||
</New>
|
</New>
|
||||||
</Arg>
|
</Arg>
|
||||||
</Call>
|
</Call>
|
||||||
|
|
|
@ -18,6 +18,17 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
|
import org.eclipse.jetty.util.annotation.Name;
|
||||||
|
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||||
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -27,66 +38,46 @@ import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
|
||||||
import org.eclipse.jetty.util.annotation.Name;
|
|
||||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
|
||||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A monitor for low resources
|
*
|
||||||
* <p>
|
* A monitor for low resources, low resources can be detected by:
|
||||||
* An instance of this class will monitor all the connectors of a server (or a set of connectors
|
|
||||||
* configured with {@link #setMonitoredConnectors(Collection)}) for a low resources state.
|
|
||||||
* <p>
|
|
||||||
* Low resources can be detected by:
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is
|
* <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is
|
||||||
* an instance of {@link ThreadPool} and {@link #setMonitorThreads(boolean)} is true.</li>
|
* an instance of {@link ThreadPool} and {@link #setMonitorThreads(boolean)} is true.</li>
|
||||||
* <li>If {@link #setMaxMemory(long)} is non zero then low resources is detected if the JVMs
|
* <li>If {@link #setMaxMemory(long)} is non zero then low resources is detected if the JVMs
|
||||||
* {@link Runtime} instance has {@link Runtime#totalMemory()} minus {@link Runtime#freeMemory()}
|
* {@link Runtime} instance has {@link Runtime#totalMemory()} minus {@link Runtime#freeMemory()}
|
||||||
* greater than {@link #getMaxMemory()}</li>
|
* greater than {@link #getMaxMemory()}</li>
|
||||||
* <li>If {@link #setMaxConnections(int)} is non zero then low resources is dected if the total number
|
* <li>If {@link #setMaxConnections(int)} is non zero then low resources is detected if the total number
|
||||||
* of connections exceeds {@link #getMaxConnections()}. This feature is deprecated and replaced by
|
* of connections exceeds {@link #getMaxConnections()}. This feature is deprecated and replaced by
|
||||||
* {@link ConnectionLimit}</li>
|
* {@link ConnectionLimit}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
*
|
||||||
* Once low resources state is detected, the cause is logged and all existing connections returned
|
|
||||||
* by {@link Connector#getConnectedEndPoints()} have {@link EndPoint#setIdleTimeout(long)} set
|
|
||||||
* to {@link #getLowResourcesIdleTimeout()}. New connections are not affected, however if the low
|
|
||||||
* resources state persists for more than {@link #getMaxLowResourcesTime()}, then the
|
|
||||||
* {@link #getLowResourcesIdleTimeout()} to all connections again. Once the low resources state is
|
|
||||||
* cleared, the idle timeout is reset to the connector default given by {@link Connector#getIdleTimeout()}.
|
|
||||||
* <p>
|
|
||||||
* If {@link #setAcceptingInLowResources(boolean)} is set to false (Default is true), then no new connections
|
|
||||||
* are accepted when in low resources state.
|
|
||||||
*/
|
*/
|
||||||
@ManagedObject ("Monitor for low resource conditions and activate a low resource mode if detected")
|
@ManagedObject ("Monitor for low resource conditions and activate a low resource mode if detected")
|
||||||
public class LowResourceMonitor extends AbstractLifeCycle
|
public class LowResourceMonitor extends ContainerLifeCycle
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(LowResourceMonitor.class);
|
private static final Logger LOG = Log.getLogger(LowResourceMonitor.class);
|
||||||
private final Server _server;
|
|
||||||
|
protected final Server _server;
|
||||||
private Scheduler _scheduler;
|
private Scheduler _scheduler;
|
||||||
private Connector[] _monitoredConnectors;
|
private Connector[] _monitoredConnectors;
|
||||||
private Set<AbstractConnector> _acceptingConnectors = new HashSet<>();
|
private Set<AbstractConnector> _acceptingConnectors = new HashSet<>();
|
||||||
private int _period=1000;
|
private int _period=1000;
|
||||||
private int _maxConnections;
|
|
||||||
private long _maxMemory;
|
|
||||||
private int _lowResourcesIdleTimeout=1000;
|
private int _lowResourcesIdleTimeout=1000;
|
||||||
private int _maxLowResourcesTime=0;
|
private int _maxLowResourcesTime=0;
|
||||||
private boolean _monitorThreads=true;
|
|
||||||
private final AtomicBoolean _low = new AtomicBoolean();
|
private final AtomicBoolean _low = new AtomicBoolean();
|
||||||
private String _cause;
|
|
||||||
private String _reasons;
|
private String _reasons;
|
||||||
|
|
||||||
private long _lowStarted;
|
private long _lowStarted;
|
||||||
private boolean _acceptingInLowResources = true;
|
private boolean _acceptingInLowResources = true;
|
||||||
|
|
||||||
|
private Set<LowResourceCheck> _lowResourceChecks = new HashSet<>();
|
||||||
|
|
||||||
private final Runnable _monitor = new Runnable()
|
private final Runnable _monitor = new Runnable()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -95,14 +86,86 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
if (isRunning())
|
if (isRunning())
|
||||||
{
|
{
|
||||||
monitor();
|
monitor();
|
||||||
_scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS);
|
_scheduler.schedule( _monitor, _period, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public LowResourceMonitor(@Name("server") Server server)
|
public LowResourceMonitor(@Name("server") Server server)
|
||||||
{
|
{
|
||||||
_server=server;
|
_server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManagedAttribute("True if low available threads status is monitored")
|
||||||
|
public boolean getMonitorThreads()
|
||||||
|
{
|
||||||
|
return !getBeans(ConnectorsThreadPoolLowResourceCheck.class).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param monitorThreads If true, check connectors executors to see if they are
|
||||||
|
* {@link ThreadPool} instances that are low on threads.
|
||||||
|
*/
|
||||||
|
public void setMonitorThreads(boolean monitorThreads)
|
||||||
|
{
|
||||||
|
if(monitorThreads)
|
||||||
|
// already configured?
|
||||||
|
if ( !getMonitorThreads() ) addLowResourceCheck( new ConnectorsThreadPoolLowResourceCheck() );
|
||||||
|
else
|
||||||
|
getBeans(ConnectorsThreadPoolLowResourceCheck.class).forEach(this::removeBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The maximum connections allowed for the monitored connectors before low resource handling is activated
|
||||||
|
* @deprecated Replaced by ConnectionLimit
|
||||||
|
*/
|
||||||
|
@ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
|
||||||
|
@Deprecated
|
||||||
|
public int getMaxConnections()
|
||||||
|
{
|
||||||
|
for(MaxConnectionsLowResourceCheck lowResourceCheck : getBeans(MaxConnectionsLowResourceCheck.class))
|
||||||
|
{
|
||||||
|
if (lowResourceCheck.getMaxConnections()>0)
|
||||||
|
{
|
||||||
|
return lowResourceCheck.getMaxConnections();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param maxConnections The maximum connections before low resources state is triggered
|
||||||
|
* @deprecated Replaced by ConnectionLimit
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setMaxConnections(int maxConnections)
|
||||||
|
{
|
||||||
|
if (maxConnections>0)
|
||||||
|
{
|
||||||
|
if (getBeans(MaxConnectionsLowResourceCheck.class).isEmpty())
|
||||||
|
{
|
||||||
|
addLowResourceCheck(new MaxConnectionsLowResourceCheck(maxConnections));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
getBeans(MaxConnectionsLowResourceCheck.class).forEach( c -> c.setMaxConnections( maxConnections ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getBeans(ConnectorsThreadPoolLowResourceCheck.class).forEach(this::removeBean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ManagedAttribute("The reasons the monitored connectors are low on resources")
|
||||||
|
public String getReasons()
|
||||||
|
{
|
||||||
|
return _reasons;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setReasons(String reasons)
|
||||||
|
{
|
||||||
|
_reasons = reasons;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ManagedAttribute("Are the monitored connectors low on resources?")
|
@ManagedAttribute("Are the monitored connectors low on resources?")
|
||||||
|
@ -111,24 +174,39 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
return _low.get();
|
return _low.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean enableLowOnResources(boolean expectedValue, boolean newValue)
|
||||||
|
{
|
||||||
|
return _low.compareAndSet(expectedValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
@ManagedAttribute("The reason(s) the monitored connectors are low on resources")
|
@ManagedAttribute("The reason(s) the monitored connectors are low on resources")
|
||||||
public String getLowResourcesReasons()
|
public String getLowResourcesReasons()
|
||||||
{
|
{
|
||||||
return _reasons;
|
return _reasons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setLowResourcesReasons(String reasons)
|
||||||
|
{
|
||||||
|
_reasons = reasons;
|
||||||
|
}
|
||||||
|
|
||||||
@ManagedAttribute("Get the timestamp in ms since epoch that low resources state started")
|
@ManagedAttribute("Get the timestamp in ms since epoch that low resources state started")
|
||||||
public long getLowResourcesStarted()
|
public long getLowResourcesStarted()
|
||||||
{
|
{
|
||||||
return _lowStarted;
|
return _lowStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLowResourcesStarted(long lowStarted)
|
||||||
|
{
|
||||||
|
_lowStarted = lowStarted;
|
||||||
|
}
|
||||||
|
|
||||||
@ManagedAttribute("The monitored connectors. If null then all server connectors are monitored")
|
@ManagedAttribute("The monitored connectors. If null then all server connectors are monitored")
|
||||||
public Collection<Connector> getMonitoredConnectors()
|
public Collection<Connector> getMonitoredConnectors()
|
||||||
{
|
{
|
||||||
if (_monitoredConnectors==null)
|
if (_monitoredConnectors==null)
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
return Arrays.asList(_monitoredConnectors);
|
return Arrays.asList( _monitoredConnectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,6 +220,13 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
_monitoredConnectors = monitoredConnectors.toArray(new Connector[monitoredConnectors.size()]);
|
_monitoredConnectors = monitoredConnectors.toArray(new Connector[monitoredConnectors.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Connector[] getMonitoredOrServerConnectors()
|
||||||
|
{
|
||||||
|
if (_monitoredConnectors!=null && _monitoredConnectors.length>0)
|
||||||
|
return _monitoredConnectors;
|
||||||
|
return _server.getConnectors();
|
||||||
|
}
|
||||||
|
|
||||||
@ManagedAttribute("If false, new connections are not accepted while in low resources")
|
@ManagedAttribute("If false, new connections are not accepted while in low resources")
|
||||||
public boolean isAcceptingInLowResources()
|
public boolean isAcceptingInLowResources()
|
||||||
{
|
{
|
||||||
|
@ -152,7 +237,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
{
|
{
|
||||||
_acceptingInLowResources = acceptingInLowResources;
|
_acceptingInLowResources = acceptingInLowResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ManagedAttribute("The monitor period in ms")
|
@ManagedAttribute("The monitor period in ms")
|
||||||
public int getPeriod()
|
public int getPeriod()
|
||||||
{
|
{
|
||||||
|
@ -167,58 +252,6 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
_period = periodMS;
|
_period = periodMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ManagedAttribute("True if low available threads status is monitored")
|
|
||||||
public boolean getMonitorThreads()
|
|
||||||
{
|
|
||||||
return _monitorThreads;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param monitorThreads If true, check connectors executors to see if they are
|
|
||||||
* {@link ThreadPool} instances that are low on threads.
|
|
||||||
*/
|
|
||||||
public void setMonitorThreads(boolean monitorThreads)
|
|
||||||
{
|
|
||||||
_monitorThreads = monitorThreads;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The maximum connections allowed for the monitored connectors before low resource handling is activated
|
|
||||||
* @deprecated Replaced by ConnectionLimit
|
|
||||||
*/
|
|
||||||
@ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
|
|
||||||
@Deprecated
|
|
||||||
public int getMaxConnections()
|
|
||||||
{
|
|
||||||
return _maxConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param maxConnections The maximum connections before low resources state is triggered
|
|
||||||
* @deprecated Replaced by ConnectionLimit
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setMaxConnections(int maxConnections)
|
|
||||||
{
|
|
||||||
if (maxConnections>0)
|
|
||||||
LOG.warn("LowResourceMonitor.setMaxConnections is deprecated. Use ConnectionLimit.");
|
|
||||||
_maxConnections = maxConnections;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ManagedAttribute("The maximum memory (in bytes) that can be used before low resources is triggered. Memory used is calculated as (totalMemory-freeMemory).")
|
|
||||||
public long getMaxMemory()
|
|
||||||
{
|
|
||||||
return _maxMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param maxMemoryBytes The maximum memory in bytes in use before low resources is triggered.
|
|
||||||
*/
|
|
||||||
public void setMaxMemory(long maxMemoryBytes)
|
|
||||||
{
|
|
||||||
_maxMemory = maxMemoryBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ManagedAttribute("The idletimeout in ms to apply to all existing connections when low resources is detected")
|
@ManagedAttribute("The idletimeout in ms to apply to all existing connections when low resources is detected")
|
||||||
public int getLowResourcesIdleTimeout()
|
public int getLowResourcesIdleTimeout()
|
||||||
{
|
{
|
||||||
|
@ -247,6 +280,99 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
_maxLowResourcesTime = maxLowResourcesTimeMS;
|
_maxLowResourcesTime = maxLowResourcesTimeMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ManagedAttribute("The maximum memory (in bytes) that can be used before low resources is triggered. Memory used is calculated as (totalMemory-freeMemory).")
|
||||||
|
public long getMaxMemory()
|
||||||
|
{
|
||||||
|
Collection<MemoryLowResourceCheck> beans = getBeans(MemoryLowResourceCheck.class);
|
||||||
|
if(beans.isEmpty())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return beans.stream().findFirst().get().getMaxMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param maxMemoryBytes The maximum memory in bytes in use before low resources is triggered.
|
||||||
|
*/
|
||||||
|
public void setMaxMemory(long maxMemoryBytes)
|
||||||
|
{
|
||||||
|
if(maxMemoryBytes<=0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Collection<MemoryLowResourceCheck> beans = getBeans(MemoryLowResourceCheck.class);
|
||||||
|
if(beans.isEmpty())
|
||||||
|
addLowResourceCheck( new MemoryLowResourceCheck( maxMemoryBytes ) );
|
||||||
|
else
|
||||||
|
beans.forEach( lowResourceCheck -> lowResourceCheck.setMaxMemory( maxMemoryBytes ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<LowResourceCheck> getLowResourceChecks()
|
||||||
|
{
|
||||||
|
return _lowResourceChecks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLowResourceChecks( Set<LowResourceCheck> lowResourceChecks )
|
||||||
|
{
|
||||||
|
updateBeans(_lowResourceChecks.toArray(),lowResourceChecks.toArray());
|
||||||
|
this._lowResourceChecks = lowResourceChecks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLowResourceCheck( LowResourceCheck lowResourceCheck )
|
||||||
|
{
|
||||||
|
addBean(lowResourceCheck);
|
||||||
|
this._lowResourceChecks.add(lowResourceCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void monitor()
|
||||||
|
{
|
||||||
|
|
||||||
|
String reasons=null;
|
||||||
|
|
||||||
|
|
||||||
|
for(LowResourceCheck lowResourceCheck : _lowResourceChecks)
|
||||||
|
{
|
||||||
|
if(lowResourceCheck.isLowOnResources())
|
||||||
|
{
|
||||||
|
reasons = lowResourceCheck.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reasons!=null)
|
||||||
|
{
|
||||||
|
// Log the reasons if there is any change in the cause
|
||||||
|
if (!reasons.equals(getReasons()))
|
||||||
|
{
|
||||||
|
LOG.warn("Low Resources: {}",reasons);
|
||||||
|
setReasons(reasons);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter low resources state?
|
||||||
|
if (enableLowOnResources(false,true))
|
||||||
|
{
|
||||||
|
setLowResourcesReasons(reasons);
|
||||||
|
setLowResourcesStarted(System.currentTimeMillis());
|
||||||
|
setLowResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Too long in low resources state?
|
||||||
|
if ( getMaxLowResourcesTime()>0 && (System.currentTimeMillis()-getLowResourcesStarted())>getMaxLowResourcesTime())
|
||||||
|
setLowResources();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (enableLowOnResources(true,false))
|
||||||
|
{
|
||||||
|
LOG.info("Low Resources cleared");
|
||||||
|
setLowResourcesReasons(null);
|
||||||
|
setLowResourcesStarted(0);
|
||||||
|
setReasons(null);
|
||||||
|
clearLowResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doStart() throws Exception
|
protected void doStart() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -257,6 +383,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
_scheduler=new LRMScheduler();
|
_scheduler=new LRMScheduler();
|
||||||
_scheduler.start();
|
_scheduler.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
super.doStart();
|
super.doStart();
|
||||||
|
|
||||||
_scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS);
|
_scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS);
|
||||||
|
@ -265,94 +392,11 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
@Override
|
@Override
|
||||||
protected void doStop() throws Exception
|
protected void doStop() throws Exception
|
||||||
{
|
{
|
||||||
if (_scheduler instanceof LRMScheduler)
|
if (_scheduler instanceof LRMScheduler )
|
||||||
_scheduler.stop();
|
_scheduler.stop();
|
||||||
super.doStop();
|
super.doStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Connector[] getMonitoredOrServerConnectors()
|
|
||||||
{
|
|
||||||
if (_monitoredConnectors!=null && _monitoredConnectors.length>0)
|
|
||||||
return _monitoredConnectors;
|
|
||||||
return _server.getConnectors();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void monitor()
|
|
||||||
{
|
|
||||||
String reasons=null;
|
|
||||||
String cause="";
|
|
||||||
int connections=0;
|
|
||||||
|
|
||||||
ThreadPool serverThreads = _server.getThreadPool();
|
|
||||||
if (_monitorThreads && serverThreads.isLowOnThreads())
|
|
||||||
{
|
|
||||||
reasons=low(reasons,"Server low on threads: "+serverThreads);
|
|
||||||
cause+="S";
|
|
||||||
}
|
|
||||||
|
|
||||||
for(Connector connector : getMonitoredOrServerConnectors())
|
|
||||||
{
|
|
||||||
connections+=connector.getConnectedEndPoints().size();
|
|
||||||
|
|
||||||
Executor executor = connector.getExecutor();
|
|
||||||
if (executor instanceof ThreadPool && executor!=serverThreads)
|
|
||||||
{
|
|
||||||
ThreadPool connectorThreads=(ThreadPool)executor;
|
|
||||||
if (_monitorThreads && connectorThreads.isLowOnThreads())
|
|
||||||
{
|
|
||||||
reasons=low(reasons,"Connector low on threads: "+connectorThreads);
|
|
||||||
cause+="T";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_maxConnections>0 && connections>_maxConnections)
|
|
||||||
{
|
|
||||||
reasons=low(reasons,"Max Connections exceeded: "+connections+">"+_maxConnections);
|
|
||||||
cause+="C";
|
|
||||||
}
|
|
||||||
|
|
||||||
long memory=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
|
|
||||||
if (_maxMemory>0 && memory>_maxMemory)
|
|
||||||
{
|
|
||||||
reasons=low(reasons,"Max memory exceeded: "+memory+">"+_maxMemory);
|
|
||||||
cause+="M";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reasons!=null)
|
|
||||||
{
|
|
||||||
// Log the reasons if there is any change in the cause
|
|
||||||
if (!cause.equals(_cause))
|
|
||||||
{
|
|
||||||
LOG.warn("Low Resources: {}",reasons);
|
|
||||||
_cause=cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter low resources state?
|
|
||||||
if (_low.compareAndSet(false,true))
|
|
||||||
{
|
|
||||||
_reasons=reasons;
|
|
||||||
_lowStarted=System.currentTimeMillis();
|
|
||||||
setLowResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Too long in low resources state?
|
|
||||||
if (_maxLowResourcesTime>0 && (System.currentTimeMillis()-_lowStarted)>_maxLowResourcesTime)
|
|
||||||
setLowResources();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_low.compareAndSet(true,false))
|
|
||||||
{
|
|
||||||
LOG.info("Low Resources cleared");
|
|
||||||
_reasons=null;
|
|
||||||
_lowStarted=0;
|
|
||||||
_cause=null;
|
|
||||||
clearLowResources();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setLowResources()
|
protected void setLowResources()
|
||||||
{
|
{
|
||||||
for(Connector connector : getMonitoredOrServerConnectors())
|
for(Connector connector : getMonitoredOrServerConnectors())
|
||||||
|
@ -366,8 +410,8 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
c.setAccepting(false);
|
c.setAccepting(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (EndPoint endPoint : connector.getConnectedEndPoints())
|
for ( EndPoint endPoint : connector.getConnectedEndPoints())
|
||||||
endPoint.setIdleTimeout(_lowResourcesIdleTimeout);
|
endPoint.setIdleTimeout(_lowResourcesIdleTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +423,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
for (EndPoint endPoint : connector.getConnectedEndPoints())
|
for (EndPoint endPoint : connector.getConnectedEndPoints())
|
||||||
endPoint.setIdleTimeout(connector.getIdleTimeout());
|
endPoint.setIdleTimeout(connector.getIdleTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AbstractConnector connector : _acceptingConnectors)
|
for (AbstractConnector connector : _acceptingConnectors)
|
||||||
{
|
{
|
||||||
connector.setAccepting(true);
|
connector.setAccepting(true);
|
||||||
|
@ -387,15 +431,219 @@ public class LowResourceMonitor extends AbstractLifeCycle
|
||||||
_acceptingConnectors.clear();
|
_acceptingConnectors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String low(String reasons, String newReason)
|
protected String low(String reasons, String newReason)
|
||||||
{
|
{
|
||||||
if (reasons==null)
|
if (reasons==null)
|
||||||
return newReason;
|
return newReason;
|
||||||
return reasons+", "+newReason;
|
return reasons+", "+newReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class LRMScheduler extends ScheduledExecutorScheduler
|
private static class LRMScheduler extends ScheduledExecutorScheduler
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LowResourceCheck
|
||||||
|
{
|
||||||
|
boolean isLowOnResources();
|
||||||
|
|
||||||
|
String getReason();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------
|
||||||
|
// default implementations for backward compat
|
||||||
|
//------------------------------------------------------
|
||||||
|
|
||||||
|
public class MainThreadPoolLowResourceCheck implements LowResourceCheck
|
||||||
|
{
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public MainThreadPoolLowResourceCheck()
|
||||||
|
{
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLowOnResources()
|
||||||
|
{
|
||||||
|
ThreadPool serverThreads = _server.getThreadPool();
|
||||||
|
if (serverThreads.isLowOnThreads())
|
||||||
|
{
|
||||||
|
reason="Server low on threads: "+serverThreads;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReason()
|
||||||
|
{
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Check if the server ThreadPool is lowOnThreads";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConnectorsThreadPoolLowResourceCheck implements LowResourceCheck
|
||||||
|
{
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public ConnectorsThreadPoolLowResourceCheck()
|
||||||
|
{
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLowOnResources()
|
||||||
|
{
|
||||||
|
ThreadPool serverThreads = _server.getThreadPool();
|
||||||
|
if(serverThreads.isLowOnThreads())
|
||||||
|
{
|
||||||
|
reason ="Server low on threads: "+serverThreads.getThreads()+", idleThreads:"+serverThreads.getIdleThreads();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for(Connector connector : getMonitoredConnectors())
|
||||||
|
{
|
||||||
|
Executor executor = connector.getExecutor();
|
||||||
|
if (executor instanceof ThreadPool && executor!=serverThreads)
|
||||||
|
{
|
||||||
|
ThreadPool connectorThreads=(ThreadPool)executor;
|
||||||
|
if (connectorThreads.isLowOnThreads())
|
||||||
|
{
|
||||||
|
reason ="Connector low on threads: "+connectorThreads;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReason()
|
||||||
|
{
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Check if the ThreadPool from monitored connectors are lowOnThreads and if all connections number is higher than the allowed maxConnection";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManagedObject("Check max allowed connections on connectors")
|
||||||
|
public class MaxConnectionsLowResourceCheck implements LowResourceCheck
|
||||||
|
{
|
||||||
|
private String reason;
|
||||||
|
private int maxConnections;
|
||||||
|
|
||||||
|
public MaxConnectionsLowResourceCheck(int maxConnections)
|
||||||
|
{
|
||||||
|
this.maxConnections = maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The maximum connections allowed for the monitored connectors before low resource handling is activated
|
||||||
|
* @deprecated Replaced by ConnectionLimit
|
||||||
|
*/
|
||||||
|
@ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
|
||||||
|
@Deprecated
|
||||||
|
public int getMaxConnections()
|
||||||
|
{
|
||||||
|
return maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param maxConnections The maximum connections before low resources state is triggered
|
||||||
|
* @deprecated Replaced by ConnectionLimit
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setMaxConnections(int maxConnections)
|
||||||
|
{
|
||||||
|
if (maxConnections>0)
|
||||||
|
LOG.warn("LowResourceMonitor.setMaxConnections is deprecated. Use ConnectionLimit.");
|
||||||
|
this.maxConnections = maxConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLowOnResources()
|
||||||
|
{
|
||||||
|
int connections=0;
|
||||||
|
for(Connector connector : getMonitoredConnectors())
|
||||||
|
{
|
||||||
|
connections+=connector.getConnectedEndPoints().size();
|
||||||
|
}
|
||||||
|
if (maxConnections>0 && connections>maxConnections)
|
||||||
|
{
|
||||||
|
reason ="Max Connections exceeded: "+connections+">"+maxConnections;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReason()
|
||||||
|
{
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "All connections number is higher than the allowed maxConnection";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemoryLowResourceCheck implements LowResourceCheck
|
||||||
|
{
|
||||||
|
private String reason;
|
||||||
|
private long maxMemory;
|
||||||
|
|
||||||
|
public MemoryLowResourceCheck(long maxMemory)
|
||||||
|
{
|
||||||
|
this.maxMemory = maxMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLowOnResources()
|
||||||
|
{
|
||||||
|
long memory=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
|
||||||
|
if (maxMemory>0 && memory>maxMemory)
|
||||||
|
{
|
||||||
|
reason = "Max memory exceeded: "+memory+">"+maxMemory;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxMemory()
|
||||||
|
{
|
||||||
|
return maxMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param maxMemoryBytes The maximum memory in bytes in use before low resources is triggered.
|
||||||
|
*/
|
||||||
|
public void setMaxMemory( long maxMemoryBytes )
|
||||||
|
{
|
||||||
|
this.maxMemory = maxMemoryBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReason()
|
||||||
|
{
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Check if used memory is higher than the allowed max memory";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2436,8 +2436,6 @@ public class Request implements HttpServletRequest
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void mergeQueryParameters(String oldQuery,String newQuery, boolean updateQueryString)
|
public void mergeQueryParameters(String oldQuery,String newQuery, boolean updateQueryString)
|
||||||
{
|
{
|
||||||
// TODO This is seriously ugly
|
|
||||||
|
|
||||||
MultiMap<String> newQueryParams = null;
|
MultiMap<String> newQueryParams = null;
|
||||||
// Have to assume ENCODING because we can't know otherwise.
|
// Have to assume ENCODING because we can't know otherwise.
|
||||||
if (newQuery!=null)
|
if (newQuery!=null)
|
||||||
|
@ -2450,7 +2448,14 @@ public class Request implements HttpServletRequest
|
||||||
if (oldQueryParams == null && oldQuery != null)
|
if (oldQueryParams == null && oldQuery != null)
|
||||||
{
|
{
|
||||||
oldQueryParams = new MultiMap<>();
|
oldQueryParams = new MultiMap<>();
|
||||||
UrlEncoded.decodeTo(oldQuery, oldQueryParams, getQueryEncoding());
|
try
|
||||||
|
{
|
||||||
|
UrlEncoded.decodeTo(oldQuery, oldQueryParams, getQueryEncoding());
|
||||||
|
}
|
||||||
|
catch(Throwable th)
|
||||||
|
{
|
||||||
|
throw new BadMessageException(400,"Bad query encoding",th);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiMap<String> mergedQueryParams;
|
MultiMap<String> mergedQueryParams;
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
|
||||||
|
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@RunWith(AdvancedRunner.class)
|
||||||
|
public class CustomResourcesMonitorTest
|
||||||
|
{
|
||||||
|
Server _server;
|
||||||
|
ServerConnector _connector;
|
||||||
|
FileOnDirectoryMonitor _fileOnDirectoryMonitor;
|
||||||
|
Path _monitoredPath;
|
||||||
|
LowResourceMonitor _lowResourceMonitor;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws Exception
|
||||||
|
{
|
||||||
|
_server = new Server();
|
||||||
|
|
||||||
|
_server.addBean(new TimerScheduler());
|
||||||
|
|
||||||
|
_connector = new ServerConnector(_server);
|
||||||
|
_connector.setPort(0);
|
||||||
|
_connector.setIdleTimeout(35000);
|
||||||
|
_server.addConnector(_connector);
|
||||||
|
|
||||||
|
_server.setHandler(new DumpHandler());
|
||||||
|
|
||||||
|
_monitoredPath = Files.createTempDirectory( "jetty_test" );
|
||||||
|
_fileOnDirectoryMonitor=new FileOnDirectoryMonitor(_monitoredPath);
|
||||||
|
_lowResourceMonitor = new LowResourceMonitor(_server);
|
||||||
|
_server.addBean( _lowResourceMonitor );
|
||||||
|
_lowResourceMonitor.addLowResourceCheck( _fileOnDirectoryMonitor );
|
||||||
|
_server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws Exception
|
||||||
|
{
|
||||||
|
_server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileOnDirectoryMonitor() throws Exception
|
||||||
|
{
|
||||||
|
int monitorPeriod = _lowResourceMonitor.getPeriod();
|
||||||
|
int lowResourcesIdleTimeout = _lowResourceMonitor.getLowResourcesIdleTimeout();
|
||||||
|
assertThat(lowResourcesIdleTimeout, Matchers.lessThanOrEqualTo(monitorPeriod));
|
||||||
|
|
||||||
|
int maxLowResourcesTime = 5 * monitorPeriod;
|
||||||
|
_lowResourceMonitor.setMaxLowResourcesTime(maxLowResourcesTime);
|
||||||
|
assertFalse(_fileOnDirectoryMonitor.isLowOnResources());
|
||||||
|
|
||||||
|
try(Socket socket0 = new Socket("localhost",_connector.getLocalPort()))
|
||||||
|
{
|
||||||
|
Path tmpFile = Files.createTempFile( _monitoredPath, "yup", ".tmp" );
|
||||||
|
// Write a file
|
||||||
|
Files.write(tmpFile, "foobar".getBytes());
|
||||||
|
|
||||||
|
// Wait a couple of monitor periods so that
|
||||||
|
// fileOnDirectoryMonitor detects it is in low mode.
|
||||||
|
Thread.sleep(2 * monitorPeriod);
|
||||||
|
assertTrue(_fileOnDirectoryMonitor.isLowOnResources());
|
||||||
|
|
||||||
|
|
||||||
|
// We already waited enough for fileOnDirectoryMonitor to close socket0.
|
||||||
|
assertEquals(-1, socket0.getInputStream().read());
|
||||||
|
|
||||||
|
// New connections are not affected by the
|
||||||
|
// low mode until maxLowResourcesTime elapses.
|
||||||
|
try(Socket socket1 = new Socket("localhost",_connector.getLocalPort()))
|
||||||
|
{
|
||||||
|
// Set a very short read timeout so we can test if the server closed.
|
||||||
|
socket1.setSoTimeout(1);
|
||||||
|
InputStream input1 = socket1.getInputStream();
|
||||||
|
|
||||||
|
assertTrue(_fileOnDirectoryMonitor.isLowOnResources());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
input1.read();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException expected)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait a couple of lowResources idleTimeouts.
|
||||||
|
Thread.sleep(2 * lowResourcesIdleTimeout);
|
||||||
|
|
||||||
|
// Verify the new socket is still open.
|
||||||
|
assertTrue(_fileOnDirectoryMonitor.isLowOnResources());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
input1.read();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException expected)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.delete( tmpFile );
|
||||||
|
|
||||||
|
// Let the maxLowResourcesTime elapse.
|
||||||
|
Thread.sleep(maxLowResourcesTime);
|
||||||
|
assertFalse(_fileOnDirectoryMonitor.isLowOnResources());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class FileOnDirectoryMonitor implements LowResourceMonitor.LowResourceCheck
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger( FileOnDirectoryMonitor.class);
|
||||||
|
|
||||||
|
private final Path _pathToMonitor;
|
||||||
|
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public FileOnDirectoryMonitor(Path pathToMonitor )
|
||||||
|
{
|
||||||
|
_pathToMonitor = pathToMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLowOnResources()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Stream<Path> paths = Files.list( _pathToMonitor );
|
||||||
|
List<Path> content = paths.collect( Collectors.toList() );
|
||||||
|
if(!content.isEmpty())
|
||||||
|
{
|
||||||
|
reason= "directory not empty so enable low resources";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( IOException e )
|
||||||
|
{
|
||||||
|
LOG.info( "ignore issue looking at directory content", e );
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReason()
|
||||||
|
{
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return getClass().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,27 +19,27 @@
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@RunWith(AdvancedRunner.class)
|
@RunWith(AdvancedRunner.class)
|
||||||
public class LowResourcesMonitorTest
|
public class LowResourcesMonitorTest
|
||||||
|
@ -48,7 +48,7 @@ public class LowResourcesMonitorTest
|
||||||
Server _server;
|
Server _server;
|
||||||
ServerConnector _connector;
|
ServerConnector _connector;
|
||||||
LowResourceMonitor _lowResourcesMonitor;
|
LowResourceMonitor _lowResourcesMonitor;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception
|
public void before() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -71,6 +71,7 @@ public class LowResourcesMonitorTest
|
||||||
_lowResourcesMonitor.setLowResourcesIdleTimeout(200);
|
_lowResourcesMonitor.setLowResourcesIdleTimeout(200);
|
||||||
_lowResourcesMonitor.setMaxConnections(20);
|
_lowResourcesMonitor.setMaxConnections(20);
|
||||||
_lowResourcesMonitor.setPeriod(900);
|
_lowResourcesMonitor.setPeriod(900);
|
||||||
|
_lowResourcesMonitor.setMonitoredConnectors( Collections.singleton( _connector ) );
|
||||||
_server.addBean(_lowResourcesMonitor);
|
_server.addBean(_lowResourcesMonitor);
|
||||||
|
|
||||||
_server.start();
|
_server.start();
|
||||||
|
@ -86,38 +87,35 @@ public class LowResourcesMonitorTest
|
||||||
@Test
|
@Test
|
||||||
public void testLowOnThreads() throws Exception
|
public void testLowOnThreads() throws Exception
|
||||||
{
|
{
|
||||||
|
_lowResourcesMonitor.setMonitorThreads(true);
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
_threadPool.setMaxThreads(_threadPool.getThreads()-_threadPool.getIdleThreads()+10);
|
_threadPool.setMaxThreads(_threadPool.getThreads()-_threadPool.getIdleThreads()+10);
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(), _lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
for (int i=0;i<100;i++)
|
for (int i=0;i<100;i++)
|
||||||
{
|
{
|
||||||
_threadPool.execute(new Runnable()
|
_threadPool.execute(() ->
|
||||||
{
|
{
|
||||||
@Override
|
try
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
try
|
latch.await();
|
||||||
{
|
}
|
||||||
latch.await();
|
catch (InterruptedException e)
|
||||||
}
|
{
|
||||||
catch (InterruptedException e)
|
e.printStackTrace();
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,10 +123,13 @@ public class LowResourcesMonitorTest
|
||||||
public void testNotAccepting() throws Exception
|
public void testNotAccepting() throws Exception
|
||||||
{
|
{
|
||||||
_lowResourcesMonitor.setAcceptingInLowResources(false);
|
_lowResourcesMonitor.setAcceptingInLowResources(false);
|
||||||
|
_lowResourcesMonitor.setMonitorThreads( true );
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
_threadPool.setMaxThreads(_threadPool.getThreads()-_threadPool.getIdleThreads()+10);
|
int maxThreads = _threadPool.getThreads()-_threadPool.getIdleThreads()+10;
|
||||||
|
System.out.println("maxThreads:"+maxThreads);
|
||||||
|
_threadPool.setMaxThreads(maxThreads);
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
|
for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
|
||||||
assertThat(c.isAccepting(),Matchers.is(true));
|
assertThat(c.isAccepting(),Matchers.is(true));
|
||||||
|
@ -136,31 +137,27 @@ public class LowResourcesMonitorTest
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
for (int i=0;i<100;i++)
|
for (int i=0;i<100;i++)
|
||||||
{
|
{
|
||||||
_threadPool.execute(new Runnable()
|
_threadPool.execute(() ->
|
||||||
{
|
{
|
||||||
@Override
|
try
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
try
|
latch.await();
|
||||||
{
|
}
|
||||||
latch.await();
|
catch (InterruptedException e)
|
||||||
}
|
{
|
||||||
catch (InterruptedException e)
|
e.printStackTrace();
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
|
for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
|
||||||
assertThat(c.isAccepting(),Matchers.is(false));
|
assertThat(c.isAccepting(),Matchers.is(false));
|
||||||
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
|
for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
|
||||||
assertThat(c.isAccepting(),Matchers.is(true));
|
assertThat(c.isAccepting(),Matchers.is(true));
|
||||||
}
|
}
|
||||||
|
@ -172,7 +169,7 @@ public class LowResourcesMonitorTest
|
||||||
{
|
{
|
||||||
_lowResourcesMonitor.setMaxMemory(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+(100*1024*1024));
|
_lowResourcesMonitor.setMaxMemory(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+(100*1024*1024));
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
byte[] data = new byte[100*1024*1024];
|
byte[] data = new byte[100*1024*1024];
|
||||||
Arrays.fill(data,(byte)1);
|
Arrays.fill(data,(byte)1);
|
||||||
|
@ -180,13 +177,13 @@ public class LowResourcesMonitorTest
|
||||||
assertThat(hash,not(equalTo(0)));
|
assertThat(hash,not(equalTo(0)));
|
||||||
|
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
data=null;
|
data=null;
|
||||||
System.gc();
|
System.gc();
|
||||||
System.gc();
|
System.gc();
|
||||||
|
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,26 +191,27 @@ public class LowResourcesMonitorTest
|
||||||
public void testMaxConnectionsAndMaxIdleTime() throws Exception
|
public void testMaxConnectionsAndMaxIdleTime() throws Exception
|
||||||
{
|
{
|
||||||
_lowResourcesMonitor.setMaxMemory(0);
|
_lowResourcesMonitor.setMaxMemory(0);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
|
assertEquals( 20, _lowResourcesMonitor.getMaxConnections() );
|
||||||
Socket[] socket = new Socket[_lowResourcesMonitor.getMaxConnections()+1];
|
Socket[] socket = new Socket[_lowResourcesMonitor.getMaxConnections()+1];
|
||||||
for (int i=0;i<socket.length;i++)
|
for (int i=0;i<socket.length;i++)
|
||||||
socket[i]=new Socket("localhost",_connector.getLocalPort());
|
socket[i]=new Socket("localhost",_connector.getLocalPort());
|
||||||
|
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
try(Socket newSocket = new Socket("localhost",_connector.getLocalPort()))
|
try(Socket newSocket = new Socket("localhost",_connector.getLocalPort()))
|
||||||
{
|
{
|
||||||
// wait for low idle time to close sockets, but not new Socket
|
// wait for low idle time to close sockets, but not new Socket
|
||||||
Thread.sleep(1200);
|
Thread.sleep(1200);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
for (int i=0;i<socket.length;i++)
|
for (int i=0;i<socket.length;i++)
|
||||||
Assert.assertEquals(-1,socket[i].getInputStream().read());
|
assertEquals(-1,socket[i].getInputStream().read());
|
||||||
|
|
||||||
newSocket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.UTF_8));
|
newSocket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.UTF_8));
|
||||||
Assert.assertEquals('H',newSocket.getInputStream().read());
|
assertEquals('H',newSocket.getInputStream().read());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,11 +220,11 @@ public class LowResourcesMonitorTest
|
||||||
{
|
{
|
||||||
int monitorPeriod = _lowResourcesMonitor.getPeriod();
|
int monitorPeriod = _lowResourcesMonitor.getPeriod();
|
||||||
int lowResourcesIdleTimeout = _lowResourcesMonitor.getLowResourcesIdleTimeout();
|
int lowResourcesIdleTimeout = _lowResourcesMonitor.getLowResourcesIdleTimeout();
|
||||||
Assert.assertThat(lowResourcesIdleTimeout, Matchers.lessThan(monitorPeriod));
|
assertThat(lowResourcesIdleTimeout, Matchers.lessThan(monitorPeriod));
|
||||||
|
|
||||||
int maxLowResourcesTime = 5 * monitorPeriod;
|
int maxLowResourcesTime = 5 * monitorPeriod;
|
||||||
_lowResourcesMonitor.setMaxLowResourcesTime(maxLowResourcesTime);
|
_lowResourcesMonitor.setMaxLowResourcesTime(maxLowResourcesTime);
|
||||||
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources());
|
assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
try(Socket socket0 = new Socket("localhost",_connector.getLocalPort()))
|
try(Socket socket0 = new Socket("localhost",_connector.getLocalPort()))
|
||||||
{
|
{
|
||||||
|
@ -236,10 +234,10 @@ public class LowResourcesMonitorTest
|
||||||
// Wait a couple of monitor periods so that
|
// Wait a couple of monitor periods so that
|
||||||
// lowResourceMonitor detects it is in low mode.
|
// lowResourceMonitor detects it is in low mode.
|
||||||
Thread.sleep(2 * monitorPeriod);
|
Thread.sleep(2 * monitorPeriod);
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
|
|
||||||
// We already waited enough for lowResourceMonitor to close socket0.
|
// We already waited enough for lowResourceMonitor to close socket0.
|
||||||
Assert.assertEquals(-1, socket0.getInputStream().read());
|
assertEquals(-1, socket0.getInputStream().read());
|
||||||
|
|
||||||
// New connections are not affected by the
|
// New connections are not affected by the
|
||||||
// low mode until maxLowResourcesTime elapses.
|
// low mode until maxLowResourcesTime elapses.
|
||||||
|
@ -249,11 +247,11 @@ public class LowResourcesMonitorTest
|
||||||
socket1.setSoTimeout(1);
|
socket1.setSoTimeout(1);
|
||||||
InputStream input1 = socket1.getInputStream();
|
InputStream input1 = socket1.getInputStream();
|
||||||
|
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
input1.read();
|
input1.read();
|
||||||
Assert.fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (SocketTimeoutException expected)
|
catch (SocketTimeoutException expected)
|
||||||
{
|
{
|
||||||
|
@ -263,22 +261,21 @@ public class LowResourcesMonitorTest
|
||||||
Thread.sleep(2 * lowResourcesIdleTimeout);
|
Thread.sleep(2 * lowResourcesIdleTimeout);
|
||||||
|
|
||||||
// Verify the new socket is still open.
|
// Verify the new socket is still open.
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
input1.read();
|
input1.read();
|
||||||
Assert.fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch (SocketTimeoutException expected)
|
catch (SocketTimeoutException expected)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let the maxLowResourcesTime elapse.
|
// Let the maxLowResourcesTime elapse.
|
||||||
Thread.sleep(maxLowResourcesTime);
|
Thread.sleep(maxLowResourcesTime);
|
||||||
|
|
||||||
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources());
|
assertTrue(_lowResourcesMonitor.isLowOnResources());
|
||||||
// Now also the new socket should be closed.
|
// Now also the new socket should be closed.
|
||||||
Assert.assertEquals(-1, input1.read());
|
assertEquals(-1, input1.read());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,8 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||||
* otherGzipFileExtensions
|
* otherGzipFileExtensions
|
||||||
* Other file extensions that signify that a file is already compressed. Eg ".svgz"
|
* Other file extensions that signify that a file is already compressed. Eg ".svgz"
|
||||||
*
|
*
|
||||||
*
|
* encodingHeaderCacheSize
|
||||||
|
* Max entries in a cache of ACCEPT-ENCODING headers.
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.servlet;
|
package org.eclipse.jetty.servlet;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -59,6 +60,8 @@ import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.eclipse.jetty.util.UrlEncoded;
|
import org.eclipse.jetty.util.UrlEncoded;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -157,6 +160,42 @@ public class DispatcherTest
|
||||||
assertEquals(expected, responses);
|
assertEquals(expected, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForwardWithBadParams() throws Exception
|
||||||
|
{
|
||||||
|
try(StacklessLogging nostack = new StacklessLogging(ServletHandler.class))
|
||||||
|
{
|
||||||
|
Log.getLogger(ServletHandler.class).info("Expect Not valid UTF8 warnings...");
|
||||||
|
_contextHandler.addServlet(AlwaysForwardServlet.class, "/forward/*");
|
||||||
|
_contextHandler.addServlet(EchoServlet.class, "/echo/*");
|
||||||
|
|
||||||
|
String response;
|
||||||
|
|
||||||
|
response = _connector.getResponse("GET /context/forward/?echo=allgood HTTP/1.0\n\n");
|
||||||
|
assertThat(response,containsString(" 200 OK"));
|
||||||
|
assertThat(response,containsString("allgood"));
|
||||||
|
|
||||||
|
response = _connector.getResponse("GET /context/forward/params?echo=allgood HTTP/1.0\n\n");
|
||||||
|
assertThat(response,containsString(" 200 OK"));
|
||||||
|
assertThat(response,containsString("allgood"));
|
||||||
|
assertThat(response,containsString("forward"));
|
||||||
|
|
||||||
|
response = _connector.getResponse("GET /context/forward/badparams?echo=badparams HTTP/1.0\n\n");
|
||||||
|
assertThat(response,containsString(" 500 "));
|
||||||
|
|
||||||
|
response = _connector.getResponse("GET /context/forward/?echo=badclient&bad=%88%A4 HTTP/1.0\n\n");
|
||||||
|
assertThat(response,containsString(" 400 "));
|
||||||
|
|
||||||
|
response = _connector.getResponse("GET /context/forward/params?echo=badclient&bad=%88%A4 HTTP/1.0\n\n");
|
||||||
|
assertThat(response,containsString(" 400 "));
|
||||||
|
|
||||||
|
response = _connector.getResponse("GET /context/forward/badparams?echo=badclientandparam&bad=%88%A4 HTTP/1.0\n\n");
|
||||||
|
assertThat(response,containsString(" 500 "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInclude() throws Exception
|
public void testInclude() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -358,6 +397,20 @@ public class DispatcherTest
|
||||||
dispatcher.forward(request, response);
|
dispatcher.forward(request, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AlwaysForwardServlet extends HttpServlet implements Servlet
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
if ("/params".equals(request.getPathInfo()))
|
||||||
|
getServletContext().getRequestDispatcher("/echo?echo=forward").forward(request, response);
|
||||||
|
else if ("/badparams".equals(request.getPathInfo()))
|
||||||
|
getServletContext().getRequestDispatcher("/echo?echo=forward&fbad=%88%A4").forward(request, response);
|
||||||
|
else
|
||||||
|
getServletContext().getRequestDispatcher("/echo").forward(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet
|
public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet
|
||||||
|
@ -484,15 +537,20 @@ public class DispatcherTest
|
||||||
@Override
|
@Override
|
||||||
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
|
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
String echoText = req.getParameter("echo");
|
String[] echoText = req.getParameterValues("echo");
|
||||||
|
|
||||||
if ( echoText == null )
|
if ( echoText == null || echoText.length==0)
|
||||||
{
|
{
|
||||||
throw new ServletException("echo is a required parameter");
|
throw new ServletException("echo is a required parameter");
|
||||||
}
|
}
|
||||||
|
else if (echoText.length==1)
|
||||||
|
{
|
||||||
|
res.getWriter().print(echoText[0]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res.getWriter().print(echoText);
|
for (String text:echoText)
|
||||||
|
res.getWriter().print(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,7 +212,19 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
|
||||||
{
|
{
|
||||||
setTrustAll(trustAll);
|
setTrustAll(trustAll);
|
||||||
addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
|
addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
|
||||||
|
|
||||||
|
// Exclude weak / insecure ciphers
|
||||||
setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$");
|
setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$");
|
||||||
|
// Exclude ciphers that don't support forward secrecy
|
||||||
|
addExcludeCipherSuites("^TLS_RSA_.*$");
|
||||||
|
// The following exclusions are present to cleanup known bad cipher
|
||||||
|
// suites that may be accidentally included via include patterns.
|
||||||
|
// The default enabled cipher list in Java will not include these
|
||||||
|
// (but they are available in the supported list).
|
||||||
|
addExcludeCipherSuites("^SSL_.*$");
|
||||||
|
addExcludeCipherSuites("^.*_NULL_.*$");
|
||||||
|
addExcludeCipherSuites("^.*_anon_.*$");
|
||||||
|
|
||||||
if (keyStorePath != null)
|
if (keyStorePath != null)
|
||||||
setKeyStorePath(keyStorePath);
|
setKeyStorePath(keyStorePath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool
|
||||||
_group = group;
|
_group = group;
|
||||||
_minThreads = minThreads;
|
_minThreads = minThreads;
|
||||||
_reservedThreads = reservedThreads;
|
_reservedThreads = reservedThreads;
|
||||||
_budget = new ThreadPoolBudget(this,minThreads);
|
_budget = new ThreadPoolBudget(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,6 +140,8 @@ public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool
|
||||||
@Override
|
@Override
|
||||||
public void setMaxThreads(int threads)
|
public void setMaxThreads(int threads)
|
||||||
{
|
{
|
||||||
|
if (_budget!=null)
|
||||||
|
_budget.check(threads);
|
||||||
_executor.setCorePoolSize(threads);
|
_executor.setCorePoolSize(threads);
|
||||||
_executor.setMaximumPoolSize(threads);
|
_executor.setMaximumPoolSize(threads);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||||
|
import org.eclipse.jetty.util.ProcessorUtils;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||||
|
@ -117,7 +118,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
}
|
}
|
||||||
_jobs=queue;
|
_jobs=queue;
|
||||||
_threadGroup=threadGroup;
|
_threadGroup=threadGroup;
|
||||||
setThreadPoolBudget(new ThreadPoolBudget(this,_minThreads));
|
setThreadPoolBudget(new ThreadPoolBudget(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -257,6 +258,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
@Override
|
@Override
|
||||||
public void setMaxThreads(int maxThreads)
|
public void setMaxThreads(int maxThreads)
|
||||||
{
|
{
|
||||||
|
if (_budget!=null)
|
||||||
|
_budget.check(maxThreads);
|
||||||
_maxThreads = maxThreads;
|
_maxThreads = maxThreads;
|
||||||
if (_minThreads > _maxThreads)
|
if (_minThreads > _maxThreads)
|
||||||
_minThreads = _maxThreads;
|
_minThreads = _maxThreads;
|
||||||
|
|
|
@ -19,13 +19,11 @@
|
||||||
package org.eclipse.jetty.util.thread;
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ProcessorUtils;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -48,8 +46,8 @@ public class ThreadPoolBudget
|
||||||
*/
|
*/
|
||||||
public class Leased implements Lease
|
public class Leased implements Lease
|
||||||
{
|
{
|
||||||
final Object leasee;
|
private final Object leasee;
|
||||||
final int threads;
|
private final int threads;
|
||||||
|
|
||||||
private Leased(Object leasee,int threads)
|
private Leased(Object leasee,int threads)
|
||||||
{
|
{
|
||||||
|
@ -66,8 +64,7 @@ public class ThreadPoolBudget
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
info.remove(this);
|
leases.remove(this);
|
||||||
allocations.remove(this);
|
|
||||||
warned.set(false);
|
warned.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +72,7 @@ public class ThreadPoolBudget
|
||||||
private static final Lease NOOP_LEASE = new Lease()
|
private static final Lease NOOP_LEASE = new Lease()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException
|
public void close()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,25 +83,26 @@ public class ThreadPoolBudget
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final ThreadPool.SizedThreadPool pool;
|
private final Set<Leased> leases = new CopyOnWriteArraySet<>();
|
||||||
final Set<Leased> allocations = new CopyOnWriteArraySet<>();
|
private final AtomicBoolean warned = new AtomicBoolean();
|
||||||
final Set<Leased> info = new CopyOnWriteArraySet<>();
|
private final ThreadPool.SizedThreadPool pool;
|
||||||
final AtomicBoolean warned = new AtomicBoolean();
|
private final int warnAt;
|
||||||
final int warnAt;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a budget for a SizedThreadPool, with the warning level set by heuristic.
|
* Construct a budget for a SizedThreadPool.
|
||||||
* @param pool The pool to budget thread allocation for.
|
* @param pool The pool to budget thread allocation for.
|
||||||
*/
|
*/
|
||||||
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool)
|
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool)
|
||||||
{
|
{
|
||||||
this(pool,Math.min(ProcessorUtils.availableProcessors(),pool.getMinThreads()));
|
this.pool = pool;
|
||||||
|
this.warnAt = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param pool The pool to budget thread allocation for.
|
* @param pool The pool to budget thread allocation for.
|
||||||
* @param warnAt The level of free threads at which a warning is generated.
|
* @param warnAt The level of free threads at which a warning is generated.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool, int warnAt)
|
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool, int warnAt)
|
||||||
{
|
{
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
|
@ -118,52 +116,60 @@ public class ThreadPoolBudget
|
||||||
|
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
allocations.clear();
|
leases.clear();
|
||||||
info.clear();
|
|
||||||
warned.set(false);
|
warned.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Lease leaseTo(Object leasee, int threads)
|
public Lease leaseTo(Object leasee, int threads)
|
||||||
{
|
{
|
||||||
Leased lease = new Leased(leasee,threads);
|
Leased lease = new Leased(leasee,threads);
|
||||||
allocations.add(lease);
|
leases.add(lease);
|
||||||
check();
|
try
|
||||||
return lease;
|
{
|
||||||
|
check(pool.getMaxThreads());
|
||||||
|
return lease;
|
||||||
|
}
|
||||||
|
catch(IllegalStateException e)
|
||||||
|
{
|
||||||
|
lease.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check registered allocations against the budget.
|
* <p>Checks leases against the given number of {@code maxThreads}.</p>
|
||||||
|
*
|
||||||
|
* @param maxThreads A proposed change to the maximum threads to check.
|
||||||
|
* @return true if passes check, false if otherwise (see logs for details)
|
||||||
* @throws IllegalStateException if insufficient threads are configured.
|
* @throws IllegalStateException if insufficient threads are configured.
|
||||||
*/
|
*/
|
||||||
public void check() throws IllegalStateException
|
public boolean check(int maxThreads) throws IllegalStateException
|
||||||
{
|
{
|
||||||
int required = allocations.stream()
|
int required = leases.stream()
|
||||||
.mapToInt(Lease::getThreads)
|
.mapToInt(Lease::getThreads)
|
||||||
.sum();
|
.sum();
|
||||||
int maximum = pool.getMaxThreads();
|
int left = maxThreads - required;
|
||||||
int actual = maximum - required;
|
if (left <= 0)
|
||||||
|
|
||||||
if (actual <= 0)
|
|
||||||
{
|
{
|
||||||
infoOnLeases();
|
printInfoOnLeases();
|
||||||
throw new IllegalStateException(String.format("Insufficient configured threads: required=%d < max=%d for %s", required, maximum, pool));
|
throw new IllegalStateException(String.format("Insufficient configured threads: required=%d < max=%d for %s", required, maxThreads, pool));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actual < warnAt)
|
if (left < warnAt)
|
||||||
{
|
{
|
||||||
infoOnLeases();
|
|
||||||
if (warned.compareAndSet(false,true))
|
if (warned.compareAndSet(false,true))
|
||||||
LOG.warn("Low configured threads: (max={} - required={})={} < warnAt={} for {}", maximum, required, actual, warnAt, pool);
|
{
|
||||||
|
printInfoOnLeases();
|
||||||
|
LOG.info("Low configured threads: (max={} - required={})={} < warnAt={} for {}", maxThreads, required, left, warnAt, pool);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void infoOnLeases()
|
private void printInfoOnLeases()
|
||||||
{
|
{
|
||||||
allocations.stream().filter(lease->!info.contains(lease))
|
leases.forEach(lease-> LOG.info("{} requires {} threads from {}",lease.leasee,lease.getThreads(),pool));
|
||||||
.forEach(lease->{
|
|
||||||
info.add(lease);
|
|
||||||
LOG.info("{} requires {} threads from {}",lease.leasee,lease.getThreads(),pool);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Lease leaseFrom(Executor executor, Object leasee, int threads)
|
public static Lease leaseFrom(Executor executor, Object leasee, int threads)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.ProcessorUtils;
|
||||||
|
import org.eclipse.jetty.util.component.LifeCycle;
|
||||||
|
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public abstract class AbstractThreadPoolTest
|
||||||
|
{
|
||||||
|
private static int originalCoreCount;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void saveState()
|
||||||
|
{
|
||||||
|
originalCoreCount = ProcessorUtils.availableProcessors();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void restoreState()
|
||||||
|
{
|
||||||
|
ProcessorUtils.setAvailableProcessors(originalCoreCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected SizedThreadPool newPool(int max);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBudget_constructMaxThenLease()
|
||||||
|
{
|
||||||
|
SizedThreadPool pool = newPool(4);
|
||||||
|
|
||||||
|
pool.getThreadPoolBudget().leaseTo(this,2);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pool.getThreadPoolBudget().leaseTo(this,3);
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch(IllegalStateException e)
|
||||||
|
{
|
||||||
|
Assert.assertThat(e.getMessage(),Matchers.containsString("Insufficient configured threads"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.getThreadPoolBudget().leaseTo(this,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBudget_LeaseThenSetMax()
|
||||||
|
{
|
||||||
|
SizedThreadPool pool = newPool(4);
|
||||||
|
|
||||||
|
pool.getThreadPoolBudget().leaseTo(this,2);
|
||||||
|
|
||||||
|
pool.setMaxThreads(3);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pool.setMaxThreads(1);
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
catch(IllegalStateException e)
|
||||||
|
{
|
||||||
|
Assert.assertThat(e.getMessage(),Matchers.containsString("Insufficient configured threads"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertThat(pool.getMaxThreads(),Matchers.is(3));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
|
||||||
|
|
||||||
|
public class ExecutorThreadPoolTest extends AbstractThreadPoolTest
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected SizedThreadPool newPool(int max)
|
||||||
|
{
|
||||||
|
return new ExecutorThreadPool(max);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,16 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.util.thread;
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
|
||||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -35,8 +25,17 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@RunWith(AdvancedRunner.class)
|
import java.util.concurrent.CountDownLatch;
|
||||||
public class QueuedThreadPoolTest
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.ProcessorUtils;
|
||||||
|
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||||
|
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
||||||
{
|
{
|
||||||
private final AtomicInteger _jobs=new AtomicInteger();
|
private final AtomicInteger _jobs=new AtomicInteger();
|
||||||
|
|
||||||
|
@ -297,4 +296,11 @@ public class QueuedThreadPoolTest
|
||||||
{
|
{
|
||||||
new QueuedThreadPool(4, 8);
|
new QueuedThreadPool(4, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SizedThreadPool newPool(int max)
|
||||||
|
{
|
||||||
|
return new QueuedThreadPool(max);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,9 +95,10 @@
|
||||||
* found ending with ".gz" (default false)
|
* found ending with ".gz" (default false)
|
||||||
* (deprecated: use precompressed)
|
* (deprecated: use precompressed)
|
||||||
*
|
*
|
||||||
* precompressed If set to a comma separated list of file extensions, these
|
* precompressed If set to a comma separated list of encoding types (that may be
|
||||||
* indicate compressed formats that are known to map to a mime-type
|
* listed in a requests Accept-Encoding header) to file
|
||||||
* that may be listed in a requests Accept-Encoding header.
|
* extension mappings to look for and serve. For example:
|
||||||
|
* "br=.br,gzip=.gz,bzip2=.bz".
|
||||||
* If set to a boolean True, then a default set of compressed formats
|
* If set to a boolean True, then a default set of compressed formats
|
||||||
* will be used, otherwise no precompressed formats.
|
* will be used, otherwise no precompressed formats.
|
||||||
*
|
*
|
||||||
|
@ -115,9 +116,6 @@
|
||||||
*
|
*
|
||||||
* stylesheet Set with the location of an optional stylesheet that will be used
|
* stylesheet Set with the location of an optional stylesheet that will be used
|
||||||
* to decorate the directory listing html.
|
* to decorate the directory listing html.
|
||||||
*
|
|
||||||
* aliases If True, aliases of resources are allowed (eg. symbolic
|
|
||||||
* links and caps variations). May bypass security constraints.
|
|
||||||
*
|
*
|
||||||
* etags If True, weak etags will be generated and handled.
|
* etags If True, weak etags will be generated and handled.
|
||||||
*
|
*
|
||||||
|
@ -134,15 +132,17 @@
|
||||||
* cacheControl If set, all static content will have this value set as the cache-control
|
* cacheControl If set, all static content will have this value set as the cache-control
|
||||||
* header.
|
* header.
|
||||||
*
|
*
|
||||||
|
* encodingHeaderCacheSize
|
||||||
|
* Max entries in a cache of ACCEPT-ENCODING headers.
|
||||||
|
*
|
||||||
|
* otherGzipFileExtensions
|
||||||
|
* defaults to .svgz but a comma separated list of gzip equivalent file extensions can be supplied
|
||||||
|
*
|
||||||
-->
|
-->
|
||||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>default</servlet-name>
|
<servlet-name>default</servlet-name>
|
||||||
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
|
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
|
||||||
<init-param>
|
|
||||||
<param-name>aliases</param-name>
|
|
||||||
<param-value>false</param-value>
|
|
||||||
</init-param>
|
|
||||||
<init-param>
|
<init-param>
|
||||||
<param-name>acceptRanges</param-name>
|
<param-name>acceptRanges</param-name>
|
||||||
<param-value>true</param-value>
|
<param-value>true</param-value>
|
||||||
|
@ -171,12 +171,6 @@
|
||||||
<param-name>maxCachedFiles</param-name>
|
<param-name>maxCachedFiles</param-name>
|
||||||
<param-value>2048</param-value>
|
<param-value>2048</param-value>
|
||||||
</init-param>
|
</init-param>
|
||||||
<!--
|
|
||||||
<init-param>
|
|
||||||
<param-name>precompressed</param-name>
|
|
||||||
<param-value>gzip,br</param-value>
|
|
||||||
</init-param>
|
|
||||||
-->
|
|
||||||
<init-param>
|
<init-param>
|
||||||
<param-name>etags</param-name>
|
<param-name>etags</param-name>
|
||||||
<param-value>false</param-value>
|
<param-value>false</param-value>
|
||||||
|
@ -185,18 +179,6 @@
|
||||||
<param-name>useFileMappedBuffer</param-name>
|
<param-name>useFileMappedBuffer</param-name>
|
||||||
<param-value>true</param-value>
|
<param-value>true</param-value>
|
||||||
</init-param>
|
</init-param>
|
||||||
<!--
|
|
||||||
<init-param>
|
|
||||||
<param-name>resourceCache</param-name>
|
|
||||||
<param-value>resourceCache</param-value>
|
|
||||||
</init-param>
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<init-param>
|
|
||||||
<param-name>cacheControl</param-name>
|
|
||||||
<param-value>max-age=3600,public</param-value>
|
|
||||||
</init-param>
|
|
||||||
-->
|
|
||||||
<load-on-startup>0</load-on-startup>
|
<load-on-startup>0</load-on-startup>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,14 @@
|
||||||
<param-value>a context value</param-value>
|
<param-value>a context value</param-value>
|
||||||
</context-param>
|
</context-param>
|
||||||
|
|
||||||
|
<!-- Add or override servlet init parameter -->
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>default</servlet-name>
|
||||||
|
<init-param>
|
||||||
|
<param-name>precompressed</param-name>
|
||||||
|
<param-value>true</param-value>
|
||||||
|
</init-param>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
<!-- Add or override servlet init parameter -->
|
<!-- Add or override servlet init parameter -->
|
||||||
<servlet>
|
<servlet>
|
||||||
|
|
Loading…
Reference in New Issue