JBossWeb/Tomcat HTTP Session Implementation Details: Difference between revisions
(12 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
=External= | |||
* How does JBoss EAP manage sessions in distribution mode? https://access.redhat.com/solutions/2482211 | |||
=Internal= | =Internal= | ||
* [[HTTP_Session#Session_Implementation_Details|HTTP Session]] | * [[HTTP_Session#Session_Implementation_Details|HTTP Session]] | ||
Line 6: | Line 11: | ||
* EAP 6.4.6 | * EAP 6.4.6 | ||
* JBoss AS 5.1.2 | * JBoss AS 5.1.2 | ||
=Subjects= | |||
<blockquote style="background-color: #f9f9f9; border: solid thin lightgrey;"> | |||
:[[Web Application Monitoring in WildFly#Active_HTTP_Session_Count|Active HTTP Session Count]] | |||
</blockquote> | |||
=Clustered Session Diagram= | =Clustered Session Diagram= | ||
Line 81: | Line 92: | ||
=SessionID Generation= | =SessionID Generation= | ||
SessionID is generated by <tt>ManagerBase.generateSessionId()</tt>. | SessionID is generated by <tt>org.apache.catalina.session.ManagerBase.generateSessionId()</tt>. | ||
<pre> | |||
/** | |||
* Generate and return a new session identifier. | |||
*/ | |||
protected String generateSessionId(Random random) { | |||
byte[] bytes = new byte[sessionIdLength]; | |||
random.nextBytes(bytes); | |||
// Encode the result | |||
char[] id = encode(bytes); | |||
String jvmRoute = getJvmRoute(); | |||
if (appendJVMRoute() && jvmRoute != null) { | |||
StringBuilder buffer = new StringBuilder(); | |||
buffer.append(id).append('.').append(jvmRoute); | |||
return buffer.toString(); | |||
} else { | |||
return String.valueOf(id); | |||
} | |||
} | |||
</pre> | |||
=Session Counter= | =Session Counter= | ||
Line 116: | Line 147: | ||
18:14:03,571 TRACE [org.infinispan.client.hotrod.impl.RemoteCacheImpl] (http-/127.0.0.1:8480-3) About to add (K,V): (pfo9WAKHFM08w1LH-Ejxr+Yv, ImmortalCacheEntry{key=pfo9WAKHFM08w1LH-Ejxr+Yv, value=ImmortalCacheValue {value=AtomicHashMap}}) lifespanSecs:0, maxIdleSecs:0 | 18:14:03,571 TRACE [org.infinispan.client.hotrod.impl.RemoteCacheImpl] (http-/127.0.0.1:8480-3) About to add (K,V): (pfo9WAKHFM08w1LH-Ejxr+Yv, ImmortalCacheEntry{key=pfo9WAKHFM08w1LH-Ejxr+Yv, value=ImmortalCacheValue {value=AtomicHashMap}}) lifespanSecs:0, maxIdleSecs:0 | ||
</pre> | </pre> | ||
===HTTP Session Management Layer Tracing=== | |||
{{Internal|JBoss HTTP Session Troubleshooting|JBoss HTTP Session Troubleshooting}} | |||
==Who's Storing?== | ==Who's Storing?== | ||
Line 136: | Line 171: | ||
==Session Lifecycle== | ==Session Lifecycle== | ||
Session creation. | |||
<pre> | |||
get("WXVsiy0ZeTzLxXOo59WGvm4l") | |||
get("WXVsiy0ZeTzLxXOo59WGvm4l") | |||
get("UOU5nFssi1ENLGUdKUX+AO4x") | |||
get("UOU5nFssi1ENLGUdKUX+AO4x") | |||
put("WXVsiy0ZeTzLxXOo59WGvm4l", immortalCacheEntry) | |||
immortalCacheEntry: | |||
key: "WXVsiy0ZeTzLxXOo59WGvm4l" | |||
value: Map | |||
(byte)0 -> Integer(1) | |||
(byte)1 -> Long(...) | |||
(byte)2 -> DistributableSessionMetadata instance | |||
</pre> | |||
Write attribute 1 | |||
write(TESTKEY1,TESTVALUE1) | |||
<pre> | |||
put("WXVsiy0ZeTzLxXOo59WGvm4l", ImmortalCacheEntry instance) | |||
immortalCacheEntry: | |||
key: "WXVsiy0ZeTzLxXOo59WGvm4l" | |||
value: Map | |||
(byte)0 -> Integer(2) | |||
(byte)1 -> Long(...) | |||
(byte)2 -> DistributableSessionMetadata instance | |||
"TESTKEY1" -> "TESTVALUE1" | |||
</pre> | |||
Read attribute 1 | Read attribute 1 | ||
No cache access | |||
Write attribute 2 | Write attribute 2 | ||
<pre> | |||
put("WXVsiy0ZeTzLxXOo59WGvm4l", ImmortalCacheEntry instance) | |||
immortalCacheEntry: | |||
key: "WXVsiy0ZeTzLxXOo59WGvm4l" | |||
value: Map | |||
(byte)0 -> Integer(2) | |||
(byte)1 -> Long(...) | |||
(byte)2 -> DistributableSessionMetadata instance | |||
"TESTKEY1" -> "TESTVALUE1" | |||
"TESTKEY2" -> "TESTVALUE2" | |||
</pre> | |||
Read attribute 2 | Read attribute 2 | ||
No cache access | |||
… | … | ||
Line 158: | Line 233: | ||
Invalidate the session and remove it from cache | Invalidate the session and remove it from cache | ||
<pre> | |||
remove("WXVsiy0ZeTzLxXOo59WGvm4l") | |||
</pre> | |||
Expiration? | Expiration? |
Latest revision as of 20:44, 7 February 2018
External
- How does JBoss EAP manage sessions in distribution mode? https://access.redhat.com/solutions/2482211
Internal
Relevance
- EAP 6.4.6
- JBoss AS 5.1.2
Subjects
Clustered Session Diagram
Lifecycle
Each org.apache.catalina.connector.Request maintains a direct reference to the active Session instance the request belongs to. The reference can be null.
The repository of sessions for a specific application (context) is an instance of org.apache.catalina.Manager. The manager is referred to from the StandardContext via the manager reference.
- If the application is not clustered, the Manager implementation is an org.apache.catalina.session.StandardManager.
- If the application is clustered, the Manager implementation is an org.jboss.as.web.session.DistributableSessionManager.
Sessions are not created automatically by the Tomcat machinery, unless we invoke HttpServletRequest.getSession() (which is equivalent with HttpServletRequest.getSession(true)) or org.apache.catalina.connector.Request.getSessionInternal().
When HttpServletRequest.getSession() is invoked, the following happen:
- The Manager instance is obtained from the StandardContext associated with the request.
- Manager.findSession(sessionId) is messaged on the Manager instance.
- The Session is looked up in the sessions map by its session ID. This applies to both non-clustered and clustered sessions.
Session Found
If the session is found, access() method is invoked on it.
If the session is not clustered, it is then simply returned.
If the session is clustered (an org.jboss.as.web.session.ClusteredSession implementation), the node attempt to acquire ownership, by invoking acquireSessionOwnership() method. For more details on session ownership, see Session Ownership and Locking.
Session Not Found
If no session is found, and org.apache.catalina.connector.Request.SESSION_ID_CHECK is true, the request tries to find the session with the ID equals to requestedSessionId among the Host's children.
If no session is found, the Manager instance is messaged to createSession(sessionId).
Common
Once the session is created by the manager (and its id generated), or it is retrieved, the request then sets a "session" cookie on Response "Set-Cookie" "JSESSIONID=FFB6...56; Path=/mycontext". The internal implementation of the cookie is TomcatCookie.
Clustered Session Access and Replication
The write entry point is org.jboss.as.web.session.ClusteredSession.setAttribute(...)
The write access consists in the following sequence:
- The state of the session is checked.
- The write arguments are checked to make sure they're are Serializable.
- The "sessionAttributesDirty" is set to true.
- The change is applied by firing HttpSessionBindingEvent instances to HttpSessionAttributeListener implementations.
This is where the interaction with the session ends. The replication is triggered either by the ClusteredSessionValve or the InstantSnapshotManager. See below:
Synchronous Replication
ClusteredSessionValve triggers replication by messaging org.jboss.as.web.session.InstantSnapshotManager.
Asynchronous Replication
Asynchronous replication is driven by org.jboss.as.web.session.ClusteredSessionValve and the snapshot-mode configuration.
If the snapshot-mode is INSTANT, after the servlet invocation, the valve asks the associated snapshot manager to "snapshot" the session on the same HTTP/AJP thread. The org.jboss.as.web.session.InstantSnapshotManager messages DistributableSessionManager to store the session. The DistributableSessionManager messages the BatchingManager and the snapshot is written in the cache.
If the snapshot-mode is INTERVAL, the replication is done periodically on a separated thread.
For more details on the concepts around synchronous/asynchronous replication see:
Common
In both cases, org.jboss.as.web.session.InstantSnapshotManager messages org.jboss.as.web.session.DistributableSessionManager instance by invoking storeSession(...).
SessionID Generation
SessionID is generated by org.apache.catalina.session.ManagerBase.generateSessionId().
/** * Generate and return a new session identifier. */ protected String generateSessionId(Random random) { byte[] bytes = new byte[sessionIdLength]; random.nextBytes(bytes); // Encode the result char[] id = encode(bytes); String jvmRoute = getJvmRoute(); if (appendJVMRoute() && jvmRoute != null) { StringBuilder buffer = new StringBuilder(); buffer.append(id).append('.').append(jvmRoute); return buffer.toString(); } else { return String.valueOf(id); } }
Session Counter
Maintained by the sessionCounter variable of the StandardManager instance of the web application.
Session Ownership and Locking
Before a node tries to use a session, it first tries to obtain the session ownership via locking, which translates into a RPC call across the cluster. That is why is almost a good idea to configure session stickiness. For more details on general concepts related to session replication and ownership, see HTTP Session Replication - Session Ownership and Stickiness.
The session ownership is acquired by the DistributedCacheManager instance from the lock manager (DistributedLockManager instance). The result of the lock acquisition attempt could be:
- NEW_LOCK
- ALREADY_HELD
- ACQUIRED_FROM_CLUSTER
The default timeout when the lock manager is trying to lock is 15,000 ms (15 seconds).
Interaction with the Underlying Cache
The session is initially retrieved from the underlying cache by getting a value associated with the session ID (example: "e7H9cWGDBFoM-KjyKpFug+uu"). The result is an org.infinispan.container.entries.ImmortalCacheEntry instance. The cache entry's key is the session ID, and the value is a map:
TRACE Logging
<logger category="org.infinispan.client.hotrod.impl.RemoteCacheImpl"> <level name="TRACE"/> </logger>
Writing:
18:14:03,571 TRACE [org.infinispan.client.hotrod.impl.RemoteCacheImpl] (http-/127.0.0.1:8480-3) About to add (K,V): (pfo9WAKHFM08w1LH-Ejxr+Yv, ImmortalCacheEntry{key=pfo9WAKHFM08w1LH-Ejxr+Yv, value=ImmortalCacheValue {value=AtomicHashMap}}) lifespanSecs:0, maxIdleSecs:0
HTTP Session Management Layer Tracing
Who's Storing?
org.infinispan.interceptors.CacheStoreInterceptor. It creates an ImmortalCacheEntry, see below:
Stored Data Structure
An ImmortalCacheEntry instance whose value is a Map:
- byte (0) - Integer (version?)
- byte (1) - Long (timestamp?)
- byte (2) - org.jboss.as.clustering.web.DistributableSessionMetadata instance.
- String id
- long creationTime
- int maxIntactiveInterval
- boolean isNew
- boolean isValid
- byte (3) - org.jboss.as.clustering.SimpleMarshalledValue instance - representing the attribute being written.
Session Lifecycle
Session creation.
get("WXVsiy0ZeTzLxXOo59WGvm4l") get("WXVsiy0ZeTzLxXOo59WGvm4l") get("UOU5nFssi1ENLGUdKUX+AO4x") get("UOU5nFssi1ENLGUdKUX+AO4x") put("WXVsiy0ZeTzLxXOo59WGvm4l", immortalCacheEntry) immortalCacheEntry: key: "WXVsiy0ZeTzLxXOo59WGvm4l" value: Map (byte)0 -> Integer(1) (byte)1 -> Long(...) (byte)2 -> DistributableSessionMetadata instance
Write attribute 1
write(TESTKEY1,TESTVALUE1)
put("WXVsiy0ZeTzLxXOo59WGvm4l", ImmortalCacheEntry instance) immortalCacheEntry: key: "WXVsiy0ZeTzLxXOo59WGvm4l" value: Map (byte)0 -> Integer(2) (byte)1 -> Long(...) (byte)2 -> DistributableSessionMetadata instance "TESTKEY1" -> "TESTVALUE1"
Read attribute 1
No cache access
Write attribute 2
put("WXVsiy0ZeTzLxXOo59WGvm4l", ImmortalCacheEntry instance) immortalCacheEntry: key: "WXVsiy0ZeTzLxXOo59WGvm4l" value: Map (byte)0 -> Integer(2) (byte)1 -> Long(...) (byte)2 -> DistributableSessionMetadata instance "TESTKEY1" -> "TESTVALUE1" "TESTKEY2" -> "TESTVALUE2"
Read attribute 2
No cache access
…
Write attribute n
Read attribute n
Loop
Invalidate the session and remove it from cache
remove("WXVsiy0ZeTzLxXOo59WGvm4l")
Expiration?