Update session programming docs

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2020-09-23 16:20:09 +02:00
parent 5a00eba2dd
commit 317d001803
12 changed files with 188 additions and 52 deletions

View File

@ -87,6 +87,11 @@
<artifactId>jetty-memcached-sessions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-nosql</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -20,16 +20,15 @@
==== Session Architecture
===== Terminology
SessionIdManager:: is responsible for allocation of session ids
HouseKeeper:: is responsible for orchestrating the detection and removal of expired sessions
SessionHandler:: is responsible for managing the lifecycle of sessions within its associated context
SessionCache:: is an L1 cache of in-use `Session` objects
Session:: is a stateful object representing a `HttpSession`
SessionData:: encapsulates the attributes and metadata associated with a `Session`
SessionDataStore:: is responsible for creating, storing and reading `SessionData`
CachingSessionDataStore:: is an L2 cache of `SessionData`
Terminology::
SessionIdManager::: is responsible for allocation of session ids
HouseKeeper::: is responsible for orchestrating the detection and removal of expired sessions
SessionHandler::: is responsible for managing the lifecycle of sessions within its associated context
SessionCache::: is an L1 cache of in-use `Session` objects
Session::: is a stateful object representing a `HttpSession`
SessionData::: encapsulates the attributes and metadata associated with a `Session`
SessionDataStore::: is responsible for creating, storing and reading `SessionData`
CachingSessionDataStore::: is an L2 cache of `SessionData`
The session architecture can be represented like so:

View File

@ -17,7 +17,7 @@
//
[[pg-server-session-cachingsessiondatastore]]
==== Session Components: The CachingSessionDataStore
===== The CachingSessionDataStore
[plantuml]
----
@ -30,13 +30,15 @@ CachingSessionDataStore "1" *-down- "1" SessionDataStore
SessionDataMap <|-- MemcachedSessionDataMap
----
The `CachingSessionDataStore` is a special type of `SessionDataStore` that checks an L2 cache for `SessionData` before checking a delegate `SessionDataStore`.
The link:{JDURL}/org/eclipse/jetty/server/session/CachingSessionDataStore.html[CachingSessionDataStore] is a special type of `SessionDataStore` that checks an L2 cache for `SessionData` before checking a delegate `SessionDataStore`.
This can improve the performance of slow stores.
The L2 cache is an instance of a link:{JDURL}/org/eclipse/jetty/server/session/SessionDataMap.html[SessionDataMap].
Jetty provides one implementation of this L2 cache based on `memcached`, link:{JDURL}/org/eclipse/jetty/memcached/session/MemcachedSessionDataMap.html[MemcachedSessionDataMap].
Here's an example of how to programmatically configure ``CachingSessionDataStore``s, using a `FileSessionDataStore` as a delegate, and `memcached` as the L2 cache:
====== Configuration
Here's an example of how to programmatically configure ``CachingSessionDataStore``s, using a xref:pg-server-session-datastore-file[FileSessionDataStore] as a delegate, and `memcached` as the L2 cache:
[source,java,indent=0]
----

View File

@ -16,8 +16,8 @@
// ========================================================================
//
[[pg-server-session-sessioncache]]
==== Session Components: The SessionCache
[[pg-server-session-cache]]
==== The SessionCache
There is one `SessionCache` per `SessionHandler`, and thus one per context.
Its purpose is to provide an L1 cache of `Session` objects.
@ -26,13 +26,14 @@ A `SessionCache` uses a `SessionDataStore` to create, read, store and delete the
There are two ways to create a `SessionCache` for a `SessionHandler`:
. allow the `SessionHandler` to create one lazily at startup
. allow the `SessionHandler` to create one lazily at startup.
The `SessionHandler` looks for a `SessionCacheFactory` bean on the server to produce the `SessionCache` instance.
It then looks for a `SessionDataStoreFactory` bean on the server to produce a `SessionDataStore` instance to use with the `SessionCache`.
. pass a fully configured `SessionCache` instance to the `SessionHandler`
. pass a fully configured `SessionCache` instance to the `SessionHandler`.
You are responsible for configuring both the `SessionCache` instance and its `SessionDataStore`
More on ``SessionDataStore``s xref:pg-server-session-sessiondatastore[later], in this section we will concentrate on the `SessionCache` and `SessionCacheFactory`.
More on ``SessionDataStore``s xref:pg-server-session-datastore[later], in this section we will concentrate on the `SessionCache` and `SessionCacheFactory`.
The link:{JDURL}/org/eclipse/jetty/server/session/AbstractSessionCache.html[AbstractSessionCache] provides most of the behaviour of ``SessionCache``s.
@ -56,7 +57,7 @@ The eviction strategies are:
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[skip some writes], this option ensures that the session will be written out.
However, as `SessionDataStores` can be configured to xref:pg-server-session-datastore-skip[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.
@ -83,7 +84,7 @@ Jetty provides two `SessionCache` implementations: the link:{JDURL}/org/eclipse/
[[pg-server-session-hash]]
===== The DefaultSessionCache
The `DefaultSessionCache` retains `Session` objects in memory in a `ConcurrentHashMap`.
The link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionCache.html[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.
@ -113,16 +114,16 @@ Alternatively, if you only have a single `SessionHandler`, or you need to config
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 create a `DefaultSessionCache`.
NOTE: If you don't configure any `SessionCache` or `SessionCacheFactory`, the `SessionHandler` will automatically create a `DefaultSessionCache`.
[[pg-server-session-null]]
===== The NullSessionCache
The `NullSessionCache` does not actually cache any objects: each request uses a fresh `Session` object.
The link:{JDURL}/org/eclipse/jetty/server/session/NullSessionCache.html[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.
As no sessions are actually cached, of course functions like `invalidateOnShutdown` and all of the eviction strategies have no meaning for the `NullSessionCache`.
There is a `NullSessionCacheFactory` which you can instantiate, configure and set as a `Server` bean to enable the `SessionHandler` to automatically create new ``NullCache``s as needed.
There is a link:{JDURL}/org/eclipse/jetty/server/session/NullSessionCacheFactory.html[NullSessionCacheFactory] which you can instantiate, configure and set as a `Server` bean to enable the `SessionHandler` to automatically create new ``NullCache``s as needed.
All of the same configuration options are available on the `NullSessionCacheFactory` as the `NullSessionCache` itself.
Alternatively, if you only have a single `SessionHandler`, or you need to configure a `NullSessionCache` differently for every `SessionHandler`, then you could dispense with the `NullSessionCacheFactory` and simply instantiate, configure and pass in the `NullSessionCache` yourself.

View File

@ -16,8 +16,8 @@
// ========================================================================
//
[[pg-server-session-sessiondatastore-file]]
==== Session Components: The FileSessionDataStore
[[pg-server-session-datastore-file]]
===== The FileSessionDataStore
The `FileSessionDataStore` supports persistent storage of session data in a filesystem.
@ -50,9 +50,9 @@ Putting all of the above together as an example, a session with an id of `node0e
`1599558193150__test_0.0.0.0_node0ek3vx7x2y1e7pmi3z00uqj1k0`
==== Configuration
====== Configuration
You can configure either a `FileSessionDataStore` individually, or a `FileSessionDataStoreFactory` if you want multiple ``SessionHandler``s to use ``FileSessionDataStore``s that are identically configured.
You can configure either a link:{JDURL}/org/eclipse/jetty/server/session/FileSessionDataStore.html[FileSessionDataStore] individually, or a `FileSessionDataStoreFactory` if you want multiple ``SessionHandler``s to use ``FileSessionDataStore``s that are identically configured.
The configuration methods are:
storeDir::
@ -67,11 +67,7 @@ Boolean, default `false`.
If set to `true`, unreadable files will be deleted.
This is useful to prevent repeated logging of the same error when the xref:pg-server-session-housekeeper[scavenger] periodically (re-)attempts to load the corrupted information for a session in order to expire it.
gracePeriod::
See xref:pg-server-session-sessiondata-grace[the gracePeriod].
savePeriod::
See xref:pg-server-session-sessiondata-skip[the savePeriod].
include::session-sessiondatastore.adoc[tag=common-datastore-config]
Let's look at an example of configuring a `FileSessionDataStoreFactory`:

View File

@ -16,8 +16,8 @@
// ========================================================================
//
[[pg-server-session-sessiondatastore-jdbc]]
==== Session Components: The JDBCSessionDataStore
[[pg-server-session-datastore-jdbc]]
===== The JDBCSessionDataStore
The `JDBCSessionDataStore` supports persistent storage of session data in a relational database.
To do that, it requires a `DatabaseAdaptor` that handles the differences between databases (eg Oracle, Postgres etc), and a `SessionTableSchema` that allows for the customization of table and column names.
@ -59,13 +59,11 @@ This is the table, with the table name, column names and type keywords at their
The name of the table and all columns can be configured using the `SessionTableSchema` class described below.
Many databases use different keywords for the `long`, `blob` and `varchar` types, so you can explicitly configure these if jetty cannot determine what they should be at runtime based on the metadata available from a db connection using the `DatabaseAdaptor` class described below.
==== Configuration
====== Configuration
gracePeriod::
See xref:pg-server-session-sessiondata-grace[the gracePeriod].
The link:{JDURL}/org/eclipse/jetty/server/session/JDBCSessionDataStore.html[JDBCSessionDataStore] and corresponding link:{JDURL}/org/eclipse/jetty/server/session/JDBCSessionDataStoreFactory.html[JDBCSessionDataStoreFactory] supports the following configuration:
savePeriod::
See xref:pg-server-session-sessiondata-skip[the savePeriod].
include::session-sessiondatastore.adoc[tag=common-datastore-config]
DatabaseAdaptor::
The `DatabaseAdaptor` can connect to a database either via a `javax.sql.Datasource` or a `java.sql.Driver`.

View File

@ -0,0 +1,106 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
[[pg-server-session-datastore-mongo]]
===== The MongoSessionDataStore
The `MongoSessionDataStore` supports persistence of `SessionData` in a nosql database.
The best description for the document model for session information is found in the javadoc for the link:{JDURL}/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.html[MongoSessionDataStore].
In overview, it can be represented thus:
[plantuml]
----
database HttpSessions {
folder jettySessions {
file session {
file "context" {
rectangle attributes
}
}
}
}
----
The database contains a document collection for the sessions.
Each document represents a session id, and contains one nested document per context in which that session id is used.
For example, the session id `abcd12345` might be used by two contexts, one with path `/contextA` and one with path `/contextB`.
In that case, the outermost document would refer to `abcd12345` and it would have a nested document for `/contextA` containing the session attributes for that context, and another nested document for `/contextB` containing the session attributes for that context.
Remember, according to the Servlet Specification, a session id can be shared by many contexts, but the attributes must be unique per context.
The outermost document contains these fields:
id::
The session id.
created::
The time (in ms since the epoch) at which the session was first created in any context.
maxIdle::
The time (in ms) for which an idle session is regarded as valid.
As maxIdle times can be different for ``Session``s from different contexts, this is the _shortest_ maxIdle time.
expiry::
The time (in ms since the epoch) at which the session will expire.
As the expiry time can be different for ``Session``s from different contexts, this is the _shortest_ expiry time.
Each nested context-specific document contains:
attributes::
The session attributes as a serialized map.
lastSaved::
The time (in ms since the epoch) at which the session in this context was saved.
lastAccessed::
The time (in ms since the epoch) at which the session in this context was previously accessed.
accessed::
The time (in ms since the epoch) at which this session was most recently accessed.
lastNode::
The xref:pg-server-session-workername[workerName] of the last server that saved the session data.
version::
An object that is updated every time a session is written out for a context.
====== Configuration
You can configure either a link:{JDURL}/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.html[MongoSessionDataStore] individually, or a link:{JDURL}/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.html[MongoSessionDataStoreFactory] if you want multiple ``SessionHandler``s to use ``MongoSessionDataStore``s that are identically configured.
The configuration methods for the `MongoSessionDataStoreFactory` are:
include::session-sessiondatastore.adoc[tag=common-datastore-config]
dbName::
This is the name of the database.
collectionName::
The name of the document collection.
There are two alternative ways to specify the connection to mongodb:
connectionString::
This is a mongodb url, eg `mongodb://localhost`
host::
port::
This is the hostname and port number of the mongodb instance to contact.
Let's look at an example of configuring a `MongoSessionDataStoreFactory`:
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java[tags=mongosessiondatastorefactory]
----
Here's an alternate example, configuring a `MongoSessionDataStore` directly:
[source,java,indent=0]
----
include::../../{doc_code}/org/eclipse/jetty/docs/programming/server/session/SessionDocs.java[tags=mongosessiondatastore]
----

View File

@ -16,8 +16,8 @@
// ========================================================================
//
[[pg-server-session-sessiondatastore]]
==== Session Components: The SessionDataStore
[[pg-server-session-datastore]]
==== The SessionDataStore
A link:{JDURL}/org/eclipse/jetty/server/session/SessionDataStore.html[SessionDataStore] mediates the storage, retrieval and deletion of `SessionData`.
There is one `SessionDataStore` per `SessionCache`.
@ -57,7 +57,8 @@ Others store an object in shared memory, e.g. Infinispan and thus don't serializ
Whether or not a persistence technology entails passivation controls whether or not ``HttpSessionActivationListener``s will be called.
When implementing a custom `SessionDataStore` you need to decide whether or not passivation will be supported.
[[pg-server-session-sessiondata-skip]]
[[pg-server-session-datastore-skip]]
//tag::common-datastore-config[]
savePeriod::
This is an interval defined in seconds.
It is used to reduce the frequency with which `SessionData` is written.
@ -65,7 +66,7 @@ Normally, whenever the last concurrent request leaves a `Session`, the `SessionD
If the `savePeriod` is non-zero, the `SessionData` will not be persisted if no session attributes changed, _unless_ the time since the last save exceeds the `savePeriod`.
Setting a non-zero value can reduce the load on the persistence mechanism, but in a clustered environment runs the risk that other nodes will see the session as expired because it has not been persisted sufficiently recently.
[[pg-server-session-sessiondata-grace]]
[[pg-server-session-datastore-grace]]
gracePeriod::
The `gracePeriod` is an interval defined in seconds.
It is an attempt to deal with the non-transactional nature of sessions with regard to finding sessions that have expired.
@ -75,6 +76,7 @@ Thus, we use the `gracePeriod` to provide a bit of leeway around the moment of e
* on every xref:pg-server-session-housekeeper[scavenge] cycle an `AbstractSessionDataStore` searches for sessions that belong to the context that expired at least one `gracePeriod` ago
* infrequently the `AbstractSessionDataStore` searches for and summarily deletes sessions - from any context - that expired at least 10 ``gracePeriod``s ago
//end::common-datastore-config[]
NOTE:: The trivial link:{JDURL}/org/eclipse/jetty/server/session/NullSessionDataStore.html[NullSessionDataStore] - which does not persist sessions - is the default used by the `SessionHandler`.

View File

@ -16,8 +16,8 @@
// ========================================================================
//
[[pg-server-session-sessionhandler]]
==== Session Components: The SessionHandler
[[pg-server-session-handler]]
==== The SessionHandler
Each context can have a single `SessionHandler`.
The purpose of the `SessionHandler` is to interact with the `Request` and `Response` to create, maintain and propagate sessions.
@ -25,7 +25,7 @@ It also calls the context-level session listeners at appropriate points in the s
===== Configuration =====
The majority of configuration for the `SessionHandler` can be done via `web.xml` `<session-config>` declarations, or the `javax.servlet.SessionCookieConfig` api.
The majority of configuration for the link:{JDURL}/org/eclipse/jetty/server/session/SessionHandler.html[SessionHandler] can be done via `web.xml` `<session-config>` declarations, or the `javax.servlet.SessionCookieConfig` api.
There are also a few jetty-specific configuration options that we will cover here:
checkingRemoteSessionIdEncoding::
@ -50,7 +50,7 @@ This can also be configured by:
* using `javax.servlet.SessionCookieConfig.setHttpOnly(boolean)` method
* defining the `<session-config><cookie-config><http-only/></cookie-config></session-config>` element in `web.xml`
[[pg-server-session-refreshcookie]]
[[pg-server-session-handler-refreshcookie]]
refreshCookieAge::
Integer, seconds, default is `-1`.
This controls resetting the session cookie when `SessionCookieConfig.setMaxAge(int)` is non-zero.
@ -105,7 +105,7 @@ This can also be configured by:
There are also a few session settings that do not have SessionHandler setters, but can be configured with context init parameters:
[[pg-server-session-maxAge]]
[[pg-server-session-handler-maxAge]]
org.eclipse.jetty.servlet.MaxAge::
This is the maximum number of seconds that the session cookie will be considered to be valid.
By default, the cookie has no maximum validity time.

View File

@ -16,8 +16,8 @@
// ========================================================================
//
[[pg-server-session-sessionidmgr]]
==== Session Components: The SessionIdManager
[[pg-server-session-idmgr]]
==== The SessionIdManager
There is a maximum of one `SessionIdManager` per `Server` instance.
Its purpose is to generate fresh, unique session ids and to coordinate the re-use of session ids amongst co-operating contexts.
@ -25,13 +25,15 @@ Its purpose is to generate fresh, unique session ids and to coordinate the re-us
The `SessionIdManager` is agnostic with respect to the type of clustering technology chosen.
Jetty provides a default implementation - the link:{JDURL}/org/eclipse/jetty/server/session/DefaultSessionIdManager.html[DefaultSessionIdManager] - which should meet the needs of most users.
If you do not explicitly configure a `SessionIdManager`, then when the `SessionHandler` starts, it will use an instance of the `DefaultSessionIdManager`.
NOTE: If you do not explicitly configure a `SessionIdManager`, then when the `SessionHandler` starts, it will use an instance of the `DefaultSessionIdManager`.
[[pg-server-session-defaultidmgr]]
===== The DefaultSessionIdManager
At startup, if no instance of the `HouseKeeper` has been explicitly set, the `DefaultSessionIdManager` will create one.
[[pg-server-session-workername]]
Also at startup, the `workerName` is determined.
The `workerName` must be unique per `Server`, and identifies the server in a cluster.
If a `workerName` has not been explicitly set, then the value is derived as follows:
@ -65,7 +67,7 @@ This restriction is important to support cross-context dispatch.
* you should be _very_ careful to ensure that the `newSessionId(HttpServletRequest request, long created)` method does not return duplicate or predictable session ids.
[[pg-server-session-housekeeper]]
===== Session Components: The HouseKeeper
===== The HouseKeeper
There is a maximum of one link:{JDURL}/org/eclipse/jetty/server/session/HouseKeeper.html[HouseKeeper] per `SessionIdManager`.
Its purpose is to periodically poll the link:{JDURL}/org/eclipse/jetty/server/session/SessionHandler.html[SessionHandlers] to clean out expired sessions.

View File

@ -20,6 +20,8 @@
=== Session Management
Sessions are a concept within the Servlet API which allow requests to store and retrieve information across the time a user spends in an application.
Jetty provides a number of pluggable options for managing sessions.
In this section we'll look at the architecture of session support in Jetty, review the various pluggable options available and indicate what and how to customize should none of the existing options suit your usecase.
include::session-architecture.adoc[]
include::session-sessionidmgr.adoc[]
@ -28,4 +30,5 @@ include::session-sessioncache.adoc[]
include::session-sessiondatastore.adoc[]
include::session-sessiondatastore-file.adoc[]
include::session-sessiondatastore-jdbc.adoc[]
include::session-sessiondatastore-mongo.adoc[]
include::session-cachingsessiondatastore.adoc[]

View File

@ -22,6 +22,7 @@ import java.io.File;
import java.net.InetSocketAddress;
import org.eclipse.jetty.memcached.session.MemcachedSessionDataMapFactory;
import org.eclipse.jetty.nosql.mongodb.MongoSessionDataStoreFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.session.CachingSessionDataStoreFactory;
@ -288,4 +289,25 @@ public class SessionDocs
driverAdaptor.setDriverInfo("com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/sessions?user=sessionsadmin");
//end::dbaDriver[]
}
public void mongoSessionDataStore()
{
//tag::mongosdfactory[]
Server server = new Server();
MongoSessionDataStoreFactory mongoSessionDataStoreFactory = new MongoSessionDataStoreFactory();
mongoSessionDataStoreFactory.setGracePeriodSec(3600);
mongoSessionDataStoreFactory.setSavePeriodSec(0);
mongoSessionDataStoreFactory.setDbName("HttpSessions");
mongoSessionDataStoreFactory.setCollectionName("JettySessions");
//either set the connectionString
mongoSessionDataStoreFactory.setConnectionString("mongodb:://localhost");
/* or alternatively set the host and port
* mongoSessionDataStoreFactory.setHost("localhost");
* mongoSessionDatatStoreFactory.setPort(27017);
*/
//end::mongosdfactory[]
}
}