Update session docs.
Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
parent
3bb419842d
commit
2120893b76
|
@ -23,25 +23,120 @@ There is one `SessionCache` per `SessionHandler`, and thus one per context.
|
|||
Its purpose is to provide an L1 cache of `Session` objects.
|
||||
Having a working set of `Session` objects in memory allows multiple simultaneous requests for the same session to share the same `Session` object.
|
||||
A `SessionCache` uses a `SessionDataStore` to create, read, store and delete the `SessionData` associated with the `Session`.
|
||||
``SessionCache``s can be created by `SessionHandlers` as needed via a `SessionCacheFactory` registered as a bean on the `Server`.
|
||||
Alternatively, you can construct and configure an instance of a `SessionCache` and explicitly set it on a `SessionHander`.
|
||||
Be aware that in this case, you must _also_ ensure your instance is set up with a `SessionDataStore`.
|
||||
If instead the `SessionHandler` uses the `SessionCacheFactory` to create a `SessionCache` instance, it will be responsible for choosing a `SessionDataStore`: if a `SessionDataStoreFactory` bean is found on the `Server` instance, it will use that; otherwise it uses the `NullSessionDataStore`.
|
||||
More on ``SessionDataStore``s later, in this section we will concentrate the `SessionCache`.
|
||||
|
||||
|
||||
The link:{JDURL}/org/eclipse/jetty/server/session/AbstractSessionCache.html[AbstractSessionCache] provides most of the behaviour of ``SessionCache``s.
|
||||
If you are implementing a custom `SessionCache` we recommend you extend this base class, as the Servlet Specification has many subtleties and extending the base class ensures that your implementation will take account of them.
|
||||
|
||||
Some of the important behaviours of ``SessionCache``s are:
|
||||
|
||||
eviction::
|
||||
By default, sessions remain in a cache until they are expired or invalidated.
|
||||
If you have many or large sessions that are infrequently referenced you can use eviction to reduce the memory consumed by the cache.
|
||||
When a session is evicted, it is removed from the cache but it is _not_ invalidated.
|
||||
If you have configured a `SessionDataStore` that persists or distributes the session in some way, it will continue to exist, and can be read back in when it needs to be referenced again.
|
||||
The eviction strategies are:
|
||||
NEVER_EVICT:::
|
||||
This is the default, sessions remain in the cache until expired or invalidated.
|
||||
EVICT_ON_SESSION_EXIT:::
|
||||
When the last simultaneous request for a session finishes, the session will be evicted from the cache.
|
||||
EVICT_ON_INACTIVITY:::
|
||||
If a session has not been referenced for a configurable number of seconds, then it will be evicted from the cache.
|
||||
|
||||
saveOnInactiveEviction::
|
||||
This controls whether a session will be persisted to the `SessionDataStore` if it is being evicted due to the EVICT_ON_INACTIVITY policy.
|
||||
Usually sessions are written to the `SessionDataStore` whenever the last simultaneous request exits the session.
|
||||
However, as `SessionDataStores` can be configured to xref:pg-server-session-sessiondatastore[skip some writes], this option ensures that the session will be written out.
|
||||
|
||||
saveOnCreate::
|
||||
Usually a session will be written through to the configured `SessionDataStore` when the last request for it finishes.
|
||||
In the case of a freshly created session, this means that it will not be persisted until the request is fully finished.
|
||||
If your application uses context forwarding or including, the newly created session id will not be available in the subsequent contexts.
|
||||
You can enable this feature to ensure that a freshly created session is immediately persisted after creation: in this way the session id will be available for use in other contexts accessed during the same request.
|
||||
|
||||
removeUnloadableSessions::
|
||||
If a session becomes corrupted in the persistent store, it cannot be re-loaded into the `SessionCache`.
|
||||
This can cause noisy log output during scavenge cycles, when the same corrupted session fails to load over and over again.
|
||||
To prevent his, enable this feature and the `SessionCache` will ensure that if a session fails to be loaded, it will be deleted.
|
||||
|
||||
invalidateOnShutdown::
|
||||
Some applications want to ensure that all cached sessions are removed when the server shuts down.
|
||||
This option will ensure that all cached sessions are invalidated.
|
||||
The `AbstractSessionCache` does not implement this behaviour, a subclass must implement the link:{JDURL}/org/eclipse/jetty/server/session/SessionCache.html#shutdown()[SessionCache.shutdown()] method.
|
||||
|
||||
flushOnResponseCommit::
|
||||
This forces a "dirty" session to be written to the `SessionDataStore` just before a response is returned to the client, rather than waiting until the request is finished.
|
||||
This ensures that subsequent requests to either the same node or a different node see the changed session data.
|
||||
|
||||
Jetty provides two `SessionCache` implementations: the link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCache.html[DefaultSessionCache] and the link:{JDURL}/org/eclipse/jetty/server/session/NullSessionCache.html[NullSessionCache].
|
||||
|
||||
[[pg-server-session-hash]]
|
||||
===== The DefaultSessionCache
|
||||
The `DefaultSessionCache` retains `Session` objects in memory in a cache and has a number of configuration options to control cache behavior.
|
||||
It is suitable for non-clustered and clustered deployments with a sticky load balancer, as well as clustered deployments with a non-sticky load balancer, with some caveats.
|
||||
The `DefaultSessionCache` retains `Session` objects in memory in a `ConcurrentHashMap`.
|
||||
It is suitable for non-clustered and clustered deployments.
|
||||
For clustered deployments, a sticky load balancer is *strongly* recommended, otherwise you risk indeterminate session state as the session bounces around multiple nodes.
|
||||
|
||||
It implements the link:{JDURL}/org/eclipse/jetty/server/session/SessionCache.html#shutdown()[SessionCache.shutdown()] method.
|
||||
|
||||
It also provides some statistics on sessions, which are convenient to access either directly in code or remotely via jmx:
|
||||
|
||||
current sessions::
|
||||
The link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCache.html#getSessionsCurrent()[DefaultSessionCache.getSessionsCurrent()] reports the number of sessions in the cache at the time of the method call.
|
||||
|
||||
max sessions::
|
||||
The link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCache.html#getSessionsCurrent()[DefaultSessionCache.getSessionsMax()] reports the highest number of sessions in the cache at the time of the method call.
|
||||
|
||||
|
||||
total sessions::
|
||||
The link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCache.html#getSessionsTotal()[DefaultSessionCache.getSessionsTotal()] reports the cumulative total of the number of sessions in the cache at the time of the method call.
|
||||
|
||||
reset::
|
||||
The link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCache.html#resetStats()[DefaultSessionCache.resetStats()] zeros out the statistics counters.
|
||||
|
||||
As a convenience, you can use the link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.html[DefaultSessionFactory] to create a `DefaultSessionCache` whenever a new `SessionHandler` is started.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java[tags=defaultsessioncache]
|
||||
----
|
||||
|
||||
NOTE:: If you don't configure any `SessionCache` or `SessionCacheFactory`, the `SessionHandler` will automatically use a `DefaultSessionCache`.
|
||||
|
||||
[[pg-server-session-null]]
|
||||
===== The NullSessionCache
|
||||
The `NullSessionCache` does not actually cache any objects: each request uses a fresh `Session` object.
|
||||
It is suitable for clustered deployments without a sticky load balancer and non-clustered deployments when purely minimal support for sessions is needed.
|
||||
|
||||
``SessionCache``s always write out a Session to the link:{JDURL}/org/eclipse/jetty/server/session/SessionDataStore.html[SessionDataStore] whenever the last request for the `Session` exits.
|
||||
As no sessions are actually cached, of course functions like `invalidateOnShutdown` and all of the eviction strategies have no meaning for the `NullSessionCache`.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java[tags=nullsessioncache]
|
||||
----
|
||||
|
||||
[[pg-server-session-customcache]]
|
||||
===== Implementing a Custom SessionCache
|
||||
|
||||
As previously mentioned, the best thing to do is to extend the `AbstractSessionCache`.
|
||||
|
||||
===== Heterogenous Caching
|
||||
|
||||
Using the `DefaultSessionCacheFactory` or the `NullSessionCacheFactory` will ensure that every time a `SessionHandler` starts, by default it will create a new instance of the corresponding type of `SessionCache`.
|
||||
|
||||
But, what if you deploy multiple webapps, and for one of them, you don't want to use sessions?
|
||||
Or alternatively, you don't want to use sessions, but you have one webapp that now needs them?
|
||||
In that case, you can configure the factory appropriate to the majority, and then specifically create the right type of cache for the others.
|
||||
Here's an example where we configure the `DefaultSessionCacheFactory` to handle most webapps, but then specifically use a `NullSessionCache` for another:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java[tags=mixedsessioncache]
|
||||
----
|
||||
|
||||
They can also be configured to do an immediate, eager write of a freshly created session.
|
||||
This can be useful if you are likely to experience multiple, near simultaneous requests referencing the same session, e.g. with HTTP/2 and you don't have a sticky load balancer.
|
||||
Alternatively, if the eager write is not done, application paths which create and then invalidate a session within a single request never incur the cost of writing to persistent storage.
|
||||
|
||||
Additionally, if the `EVICT_ON_INACTIVITY` eviction policy is in use, you can xref:#pg-server-session-sessioncache[configure] the `DefaultSessionCache` to force a write of the `Session` to the `SessionDataStore` just before the `Session` is evicted.
|
||||
|
||||
See xref:pg-server-session-sessioncache[the L1 Session Cache] for more information.
|
||||
|
|
|
@ -19,8 +19,15 @@
|
|||
package org.eclipse.jetty.docs.programming.server.session;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionCacheFactory;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionIdManager;
|
||||
import org.eclipse.jetty.server.session.HouseKeeper;
|
||||
import org.eclipse.jetty.server.session.NullSessionCache;
|
||||
import org.eclipse.jetty.server.session.NullSessionCacheFactory;
|
||||
import org.eclipse.jetty.server.session.NullSessionDataStore;
|
||||
import org.eclipse.jetty.server.session.SessionCache;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
public class SessionDocs
|
||||
{
|
||||
|
@ -57,4 +64,79 @@ public class SessionDocs
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void defaultSessionCache()
|
||||
{
|
||||
//tag::defaultsessioncache[]
|
||||
Server server = new Server();
|
||||
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
//EVICT_ON_INACTIVE: evict a session after 60sec inactivity
|
||||
cacheFactory.setEvictionPolicy(60);
|
||||
//Only useful with the EVICT_ON_INACTIVE policy
|
||||
cacheFactory.setSaveOnInactiveEvict(true);
|
||||
cacheFactory.setFlushOnResponseCommit(true);
|
||||
cacheFactory.setInvalidateOnShutdown(false);
|
||||
cacheFactory.setRemoveUnloadableSessions(true);
|
||||
cacheFactory.setSaveOnCreate(true);
|
||||
|
||||
//Add the factory as a bean to the server, now whenever a
|
||||
//SessionHandler starts it will consult the bean to create a new DefaultSessionCache
|
||||
server.addBean(cacheFactory);
|
||||
//end::defaultsessioncache[]
|
||||
}
|
||||
|
||||
public void nullSessionCache()
|
||||
{
|
||||
//tag::nullsessioncache[]
|
||||
Server server = new Server();
|
||||
NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory();
|
||||
cacheFactory.setFlushOnResponseCommit(true);
|
||||
cacheFactory.setRemoveUnloadableSessions(true);
|
||||
cacheFactory.setSaveOnCreate(true);
|
||||
|
||||
//Add the factory as a bean to the server, now whenever a
|
||||
//SessionHandler starts it will consult the bean to create a new NullSessionCache
|
||||
server.addBean(cacheFactory);
|
||||
//end::nullsessioncache[]
|
||||
}
|
||||
|
||||
public void mixedSessionCache()
|
||||
{
|
||||
//tag::mixedsessioncache[]
|
||||
Server server = new Server();
|
||||
|
||||
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||
//NEVER_EVICT
|
||||
cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT);
|
||||
cacheFactory.setFlushOnResponseCommit(true);
|
||||
cacheFactory.setInvalidateOnShutdown(false);
|
||||
cacheFactory.setRemoveUnloadableSessions(true);
|
||||
cacheFactory.setSaveOnCreate(true);
|
||||
|
||||
//Add the factory as a bean to the server, now whenever a
|
||||
//SessionHandler starts it will consult the bean to create a new DefaultSessionCache
|
||||
server.addBean(cacheFactory);
|
||||
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
server.setHandler(contexts);
|
||||
|
||||
//Add a webapp that will use a DefaultSessionCache via the DefaultSessionCacheFactory
|
||||
WebAppContext app1 = new WebAppContext();
|
||||
app1.setContextPath("/app1");
|
||||
contexts.addHandler(app1);
|
||||
|
||||
//Add a webapp that uses an explicit NullSessionCache instead
|
||||
WebAppContext app2 = new WebAppContext();
|
||||
app2.setContextPath("/app2");
|
||||
NullSessionCache nullSessionCache = new NullSessionCache(app2.getSessionHandler());
|
||||
nullSessionCache.setFlushOnResponseCommit(true);
|
||||
nullSessionCache.setRemoveUnloadableSessions(true);
|
||||
nullSessionCache.setSaveOnCreate(true);
|
||||
//If we pass an existing SessionCache instance to the SessionHandler, it must be
|
||||
//fully configured: this means we must also provide SessionDataStore
|
||||
nullSessionCache.setSessionDataStore(new NullSessionDataStore());
|
||||
app2.getSessionHandler().setSessionCache(nullSessionCache);
|
||||
//end::mixedsessioncache[]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue