Author: markt
Date: Wed Nov 17 12:59:06 2010
New Revision: 1036019
URL: http://svn.apache.org/viewvc?rev=1036019&view=rev
Log:
Session manager performance
Focused on Windows.
Use a queue of Random's to generate session ID to remove sync bottleneck on
random.nextBytes(bytes)
Timings suggest some bottlenecks still present in default Windows code path
Modified:
tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java
tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java
tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties
tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties
tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties
tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties
tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java
tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java
tomcat/trunk/java/org/apache/catalina/session/StandardManager.java
tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java
Modified: tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java Wed Nov
17 12:59:06 2010
@@ -203,7 +203,7 @@ public class BackupManager extends Clust
}
cluster.removeManager(this);
- this.random = null;
+ this.randoms.clear();
}
@Override
Modified: tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java Wed Nov
17 12:59:06 2010
@@ -960,7 +960,7 @@ public CatalinaCluster getCluster() {
}
// Require a new random number generator if we are restarted
- this.random = null;
+ this.randoms.clear();
getCluster().removeManager(this);
replicationValve = null;
}
Modified: tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties Wed
Nov 17 12:59:06 2010
@@ -27,12 +27,13 @@ JDBCStore.checkConnectionDBClosed=The da
JDBCStore.checkConnectionDBReOpenFail=The re-open on the database failed. The
database could be down.
JDBCStore.checkConnectionSQLException=A SQL exception occurred {0}
JDBCStore.checkConnectionClassNotFoundException=JDBC driver class not found {0}
-managerBase.complete=Seeding of random number generator has been completed
+managerBase.createRandom=Created random number generator for session ID
generation in {0}ms.
+managerBase.createRandomSeed=Created SecureRandom instance to seed random
number generators for session ID generation in {0}ms.
managerBase.createSession.ise=createSession: Too many active sessions
managerBase.getting=Getting message digest component for algorithm {0}
managerBase.gotten=Completed getting message digest component
-managerBase.random=Exception initializing random number generator of class {0}
-managerBase.seeding=Seeding random number generator class {0}
+managerBase.random=Exception initializing random number generator of class
{0}. Falling back to java.util.Random.
+managerBase.seedFailed=Failed to seed random number generator class {0}
managerBase.sessionTimeout=Invalid session timeout setting {0}
serverSession.value.iae=null value
standardManager.expireException=processsExpire: Exception during session
expiration
Modified:
tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties
Wed Nov 17 12:59:06 2010
@@ -26,12 +26,10 @@ JDBCStore.checkConnectionDBClosed = La c
JDBCStore.checkConnectionDBReOpenFail = Fall\u00F3 la reapertura de la base de
datos. Puede que la base de datos est\u00E9 ca\u00EDda.
JDBCStore.checkConnectionSQLException = Ha tenido lugar una excepci\u00F3n SQL
{0}
JDBCStore.checkConnectionClassNotFoundException = No se ha hallado la clase
del manejador (driver) JDBC {0}
-managerBase.complete = Se ha completado la siembra del generador de
n\u00FAmeros aleatorios
managerBase.createSession.ise = createSession\: Demasiadas sesiones activas
managerBase.getting = Obteniendo mensaje de componente de resumen (digest)
para algoritmo {0}
managerBase.gotten = Completada la obtenci\u00F3n de mensaje de componente de
resumen (digest)
managerBase.random = Excepci\u00F3n inicializando generador de n\u00FAmeros
aleatorios de clase {0}
-managerBase.seeding = Sembrando clase de generador de n\u00FAmeros aleatorios
{0}
managerBase.sessionTimeout = Valor inv\u00E1lido de Tiempo Agotado de
sesi\u00F3n {0}
serverSession.value.iae = valor nulo
standardManager.expireException = processsExpire\: Excepci\u00F3n durante la
expiraci\u00F3n de sesi\u00F3n
Modified:
tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties
Wed Nov 17 12:59:06 2010
@@ -26,12 +26,10 @@ JDBCStore.checkConnectionDBClosed=La con
JDBCStore.checkConnectionDBReOpenFail=La tentative de r\u00e9ouverture de la
base de donn\u00e9es a \u00e9chou\u00e9. La base de donn\u00e9es est
peut-\u00eatre arr\u00eat\u00e9e.
JDBCStore.checkConnectionSQLException=Une exception SQL s''est produite {0}
JDBCStore.checkConnectionClassNotFoundException=La classe du driver JDBC n''a
pas \u00e9t\u00e9 trouv\u00e9e {0}
-managerBase.complete=L''alimentation du g\u00e9n\u00e9rateur de nombre
al\u00e9atoire est termin\u00e9
managerBase.createSession.ise="createSession": Trop de sessions actives
managerBase.getting=Prise du composant d''algorithme empreinte de message
(message digest) pour l''algorithme {0}
managerBase.gotten=Prise du composant d''algorithme empreinte de message
(message digest) termin\u00e9e
managerBase.random=Exception durant l''initialisation de la classe du
g\u00e9n\u00e9rateur de nombre al\u00e9atoire {0}
-managerBase.seeding=Alimentation de la classe du g\u00e9n\u00e9rateur de
nombre al\u00e9atoire {0}
managerBase.sessionTimeout=R\u00e9glage du d\u00e9lai d''inactivit\u00e9
(timeout) de session invalide {0}
serverSession.value.iae=valeur nulle
standardManager.expireException="processsExpire": Exception lors de
l''expiration de la session
Modified:
tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties
Wed Nov 17 12:59:06 2010
@@ -27,12 +27,10 @@ JDBCStore.checkConnectionDBClosed=\u30c7
JDBCStore.checkConnectionDBReOpenFail=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u518d\u30aa\u30fc\u30d7\u30f3\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
JDBCStore.checkConnectionSQLException=SQL\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f
{0}
JDBCStore.checkConnectionClassNotFoundException=JDBC\u30c9\u30e9\u30a4\u30d0\u30af\u30e9\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
{0}
-managerBase.complete=\u4e71\u6570\u767a\u751f\u5668\u306e\u30b7\u30fc\u30c9\u306e\u751f\u6210\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
managerBase.createSession.ise=createSession:
\u30a2\u30af\u30c6\u30a3\u30d6\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u591a\u3059\u304e\u307e\u3059
managerBase.getting=\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0 {0}
\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3092\u53d6\u5f97\u3057\u307e\u3059
managerBase.gotten=\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u306e\u53d6\u5f97\u3092\u5b8c\u4e86\u3057\u307e\u3057\u305f
managerBase.random=\u30af\u30e9\u30b9 {0}
\u306e\u4e71\u6570\u767a\u751f\u5668\u306e\u521d\u671f\u5316\u306e\u4f8b\u5916\u3067\u3059
-managerBase.seeding=\u4e71\u6570\u767a\u751f\u5668\u30af\u30e9\u30b9 {0}
\u306e\u30b7\u30fc\u30c9\u3092\u751f\u6210\u3057\u3066\u3044\u307e\u3059
managerBase.sessionTimeout=\u7121\u52b9\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u8a2d\u5b9a\u3067\u3059
{0}
serverSession.value.iae=null\u5024\u3067\u3059
standardManager.expireException=processsExpire:
\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u7d42\u4e86\u51e6\u7406\u4e2d\u306e\u4f8b\u5916\u3067\u3059
Modified: tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java Wed Nov 17
12:59:06 2010
@@ -22,6 +22,8 @@ package org.apache.catalina.session;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -31,6 +33,7 @@ import java.security.AccessController;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedAction;
+import java.security.SecureRandom;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
@@ -99,8 +102,11 @@ public abstract class ManagerBase extend
/**
- * Return the MessageDigest implementation to be used when
- * creating session identifiers.
+ * Queue of MessageDigest objects to be used when creating session
+ * identifiers. If the queue is empty when a MessageDigest is required, a
+ * new MessageDigest object is created. This is designed this way since
+ * MessageDigest objects are not thread-safe and sync'ing on a single
object
+ * is slow(er).
*/
protected Queue<MessageDigest> digests =
new ConcurrentLinkedQueue<MessageDigest>();
@@ -147,14 +153,23 @@ public abstract class ManagerBase extend
/**
- * A random number generator to use when generating session identifiers.
+ * Queue of random number generator objects to be used when creating
session
+ * identifiers. If the queue is empty when a random number generator is
+ * required, a new random number generator object is created. This is
+ * designed this way since random number generator use a sync to make them
+ * thread-safe and the sync makes using a a single object slow(er).
*/
- protected volatile Random random = null;
+ protected Queue<Random> randoms = new ConcurrentLinkedQueue<Random>();
+ /**
+ * Random number generator used to see @{link {...@link #randoms}.
+ */
+ protected SecureRandom randomSeed = null;
/**
* The Java class name of the random number generator class to be used
- * when generating session identifiers.
+ * when generating session identifiers. The random number generator(s) will
+ * always be seeded from a SecureRandom instance.
*/
protected String randomClass = "java.security.SecureRandom";
@@ -597,45 +612,78 @@ public abstract class ManagerBase extend
}
/**
- * Return the random number generator instance we should use for
- * generating session identifiers. If there is no such generator
- * currently defined, construct and seed a new one.
+ * Create a new random number generator instance we should use for
+ * generating session identifiers.
*/
- public Random getRandom() {
- if (this.random == null) {
- synchronized (this) {
- if (this.random == null) {
- // Calculate the new random number generator seed
- long seed = System.currentTimeMillis();
- long t1 = seed;
- char entropy[] = getEntropy().toCharArray();
- for (int i = 0; i < entropy.length; i++) {
- long update = ((byte) entropy[i]) << ((i % 8) * 8);
- seed ^= update;
- }
- try {
- // Construct and seed a new random number generator
- Class<?> clazz = Class.forName(randomClass);
- this.random = (Random) clazz.newInstance();
- this.random.setSeed(seed);
- } catch (Exception e) {
- // Fall back to the simple case
- log.error(sm.getString("managerBase.random",
- randomClass), e);
- this.random = new java.util.Random();
- this.random.setSeed(seed);
- }
- if(log.isDebugEnabled()) {
- long t2=System.currentTimeMillis();
- if( (t2-t1) > 100 )
- log.debug(sm.getString("managerBase.seeding",
- randomClass) + " " + (t2-t1));
- }
- }
+ protected Random createRandom() {
+ if (randomSeed == null) {
+ createRandomSeed();
+ }
+
+ Random result = null;
+
+ long t1 = System.currentTimeMillis();
+ try {
+ // Construct and seed a new random number generator
+ Class<?> clazz = Class.forName(randomClass);
+ result = (Random) clazz.newInstance();
+ } catch (Exception e) {
+ // Fall back to the simple case
+ log.error(sm.getString("managerBase.random",
+ randomClass), e);
+ result = new java.util.Random();
+ }
+ byte[] seedBytes = randomSeed.generateSeed(64);
+ ByteArrayInputStream bais = new ByteArrayInputStream(seedBytes);
+ DataInputStream dis = new DataInputStream(bais);
+ for (int i = 0; i < 8; i++) {
+ try {
+ result.setSeed(dis.readLong());
+ } catch (IOException e) {
+ // Should never happen
+ log.error(sm.getString("managerBase.seedFailed",
+ result.getClass().getName()), e);
}
}
- return this.random;
+ if(log.isDebugEnabled()) {
+ long t2=System.currentTimeMillis();
+ if( (t2-t1) > 100 )
+ log.debug(sm.getString("managerBase.createRandom",
+ Long.valueOf(t2-t1)));
+ }
+ return result;
+ }
+
+
+ /**
+ * Create the random number generator that will be used to seed the random
+ * number generators that will create session IDs.
+ */
+ protected synchronized void createRandomSeed() {
+ if (randomSeed != null) {
+ return;
+ }
+
+ long seed = System.currentTimeMillis();
+ long t1 = seed;
+ char entropy[] = getEntropy().toCharArray();
+ for (int i = 0; i < entropy.length; i++) {
+ long update = ((byte) entropy[i]) << ((i % 8) * 8);
+ seed ^= update;
+ }
+
+ // Construct and seed a new random number generator
+ SecureRandom result = new SecureRandom();
+ result.setSeed(seed);
+
+ if(log.isDebugEnabled()) {
+ long t2=System.currentTimeMillis();
+ if( (t2-t1) > 100 )
+ log.debug(sm.getString("managerBase.createRandomSeed",
+ Long.valueOf(t2-t1)));
+ }
+ randomSeed = result;
}
@@ -983,7 +1031,12 @@ public abstract class ManagerBase extend
devRandomSourceIsValid = false;
closeRandomFile();
}
- getRandom().nextBytes(bytes);
+ Random random = randoms.poll();
+ if (random == null) {
+ random = createRandom();
+ }
+ random.nextBytes(bytes);
+ randoms.add(random);
}
Modified:
tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java
Wed Nov 17 12:59:06 2010
@@ -870,7 +870,7 @@ public abstract class PersistentManagerB
((Lifecycle)getStore()).stop();
// Require a new random number generator if we are restarted
- this.random = null;
+ this.randoms.clear();
}
Modified: tomcat/trunk/java/org/apache/catalina/session/StandardManager.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/StandardManager.java?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/StandardManager.java
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/StandardManager.java Wed Nov
17 12:59:06 2010
@@ -518,7 +518,7 @@ public class StandardManager extends Man
}
// Require a new random number generator if we are restarted
- this.random = null;
+ this.randoms.clear();
}
Modified: tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java?rev=1036019&r1=1036018&r2=1036019&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java (original)
+++ tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java Wed Nov 17
12:59:06 2010
@@ -31,12 +31,12 @@ public class Benchmarks extends TestCase
/*
* Results on markt's 4-core Windows dev box
- * 1 thread - ~270ms
- * 2 threads - ~350ms
- * 4 threads - ~870ms
- * 16 threads - ~3,500ms
+ * 1 thread - ~300ms
+ * 2 threads - ~330ms
+ * 4 threads - ~480ms
+ * 16 threads - ~2,200ms
*
- * Results on markt's 2-core OSX dev box
+ * Results on markt's 2-core OSX dev box (out of date)
* 1 thread - ~720ms
* 2 threads - ~1,700ms
* 4 threads - ~3,100ms
@@ -114,12 +114,12 @@ public class Benchmarks extends TestCase
/*
* Results on markt's 4-core Windows dev box
- * 1 thread - ~670ms
+ * 1 thread - ~625ms
* 2 threads - ~690ms
* 4 threads - ~1,100ms
- * 16 threads - ~5,000ms
+ * 16 threads - ~4,300ms
*
- * Results on markt's 2-core OSX dev box
+ * Results on markt's 2-core OSX dev box (out of date)
* 1 thread - ~1,500ms
* 2 threads - ~2,500ms
* 4 threads - ~4,500ms
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]