Update session docs.

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2020-09-15 15:50:47 +02:00
parent 3bb419842d
commit 2120893b76
2 changed files with 185 additions and 8 deletions

View File

@ -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.

View File

@ -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[]
}
}