Históricamente, y desde que migramos, en nuestra plataforma Linux, de Weblogic 8.1 a Weblogic 11g, el servidor en el que desplegamos nuestras aplicaciones tarda bastante en alcanzar el estado RUNNING.
Dentro de las tareas de optimización teníamos claro que había que aligerar el arranque de nuestro dominio en weblogic 11g. Descartando eliminar del dominio alguna de las aplicaciones desplegadas, nos pusimos a investigar donde se estaban perdiendo los tiempos.
Un estudio minucioso del arranque del dominio examinando las trazas nos mostró que durante un tiempo, más o menos largo dependiendo de la potencia de la máquina, weblogic quedaba parado aparentemente sin realizar ninguna operación. La última traza que se escribía, antes de la espera, era la siguiente:
<31-mar-2011 14H57’ CEST> <[STANDBY] ExecuteThread: ‘2’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1301576265940>
Las siguientes trazas, pasado el tiempo de espera, eran las siguientes:
<31-mar-2011 15H00’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589143> <31-mar-2011 15H00’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589205> <31-mar-2011 15H00’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589257>
Las trazas revelaban que entre el inicio del subsistema IIOP y el subsistema Security pasaba un tiempo de tres minutos en los que el servidor no mostraba ninguna traza. Así pues, un buen punto de partida era investigar en que se estaban empleando esos minutos de espera.
Consultando información sobre este problema en internet surgieron varios casos similares con sus respectivas soluciones.
La información corrupta de ldap.
El primer camino que investigué hablaba de corrupción en la información almacenada referente a ldap en el dominio. Esta información, supuestamente incorrecta, habría sido generada en un arranque previo.
La solución propuesta estaba basada en renombrar o directamente eliminar el directorio ldap almacenado en la instancia o instancias del servidor en el dominio. Este directorio ldap se puede encontrar en la ruta /servers//data.
Seguí este intento de solución, ya que las trazas escritas después de la parada mostraban el arranque de los recursos JPA (Java Persistence API) y JDO (Java Data Objects) cuya autenticación parecía estar basada en ldap.
Sin embargo, y tras eliminar el directorio y reiniciar el dominio, los tiempos ni mejoraron ni empeoraron, se mantuvieron similares.
Quedaba claro, por tanto, que esta no era la solución requerida.
La semilla de seguridad que germinaba tiempos de espera.
En esta bifurcación de soluciones, el siguiente camino a seguir hablaba de mal rendimiento provocado por el sistema de seguridad de java, cuya solución estaba basada en modificar el fichero java.security de la JRE con la que se arrancan los dominios.
La modificación a realizar en este fichero era sustituir la línea:
securerandom.source=file:/dev/urandom
por:
securerandom.source=file:/dev/./urandom
No entendía yo muy bien en que podía afectar este cambio al arranque de weblogic, así que escepticismo es el sentimiento que mejor definía mi estado. Sin embargo, como el subsistema que parecía estar dando los problemas era el de Security, me decidí a intentarlo.
Sorprendentemente, tras realizar el cambio y reiniciar el dominio, el tiempo de espera entre el arranque de los dos subsistemas desapareció.
<31-mar-2011 18H39’ CEST> <[STANDBY] ExecuteThread: ‘2’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1301576265940> <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589143> <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589205> <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589257> <31-mar-2011 18H39’ CEST> <[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’> <> <> <> <1302021589510>
En las trazas se puede observar que el arranque de los dos subsistemas se realiza dentro del mismo minuto, un estudio más detenido demuestra que lo hace casi en el mismo segundo.
Por tanto, estaba claro que ésta era la solución para este retraso concreto.
Sin embargo, y pese a tener la solución, no me quedaba claro el por qué de la misma, así que seguí investigando.
/dev/urandom
De esta forma me surgieron preguntas para las que no tenía respuesta. Algunas de estás preguntas eran: ¿qué era /dev/urandom? ¿para qué lo estaba usando el sistema de seguridad de java? ¿por qué ralentizaba el proceso de autenticación?
Así que comencé a buscar respuestas.
La primera respuesta que encontré fue la utilidad de /dev/urandom, un archivo especial que se puede encontrar en los sistemas unix, que basándose en los bits que genera el ruido ambiental recogido de dispositivos obtiene números pseudoaleatorios. Estos números pseudoaleatorios son ideales para utilizar en la generación de claves. Si queréis saber más podéis leer la entrada sobre /dev/random de la wikipedia. Para comprobar que era eso de la generación de números aleatorios a partir del ruido de los dispositivos, se me ocurrió hacer un cat del fichero /dev/urandom. El resultado fue el siguiente: ![]()
java.security.SecureRandom
La siguiente pregunta era saber para que estaba utilizando java el fichero /dev/urandom.
La respuesta la encontré en la clase java.security.SecureRandom. Esta es la clase que proporciona la generación de números aleatorios adecuados para implementar potentes sistemas de cifrado.
Cuando desde un desarrollo se utiliza esta clase, desde la variable securerandom.source del fichero //jre/lib/security/java.security, se recoge el sistema de generación de números aleatorios que java utilizará internamente para su generación.
La configuración, que por defecto se crea con la instalación de la jdk, es el fichero /dev/urandom como se puede comprobar en la jdk 1.6.0_21que tengo instalada en mi entorno de desarrollo.
Select the source of seed data for SecureRandom. By default an attempt is made to use the entropy gathering device specified by the securerandom.source property. If an exception occurs when accessing the URL then the traditional system/thread activity algorithm is used. On Solaris and Linux systems, if file:/dev/urandom is specified and it exists, a special SecureRandom implementation is activated by default. This “NativePRNG” reads random bytes directly from /dev/urandom. On Windows systems, the URLs file:/dev/random and file:/dev/urandom enables use of the Microsoft CryptoAPI seed functionality. securerandom.source=file:/dev/urandom
Entonces, ¿qué es lo que funciona mal?
Hasta aquí todo parece normal, ¿qué es entonces lo que no funciona?. La respuesta a esta pregunta la encontré en la base de datos de bugs que Sun (ahora Oracle) comparte en el portal bugs.sun.com. Al parecer y reportado desde la versión Java 5.0 existe el bug 6202721.
Este bug dice que si se utilizan las siguientes líneas de código en cualquier desarrollo java
SecureRandom.getInstance(“SHA1PRNG”).nextLong());
la clase SecureRandom lee las semillas del fichero /dev/random en vez del fichero /dev/urandom configurado en la opción securerandom.source del fichero java.security.
Esta casuística fue introducida como corrección de otro bug (4705093) que no he sido capaz de encontrar en la base de datos. Al parecer, este bug le daba algún tipo de significado especial a la cadena /dev/urandom que impedía utilizarla siendo sustituida por /dev/random. La utilización de éste fichero es un problema porque proporciona un rendimiento muchísimo peor.
La solución.
Existen dos soluciones propuestas a este problema.
Si somos nosotros los que hemos incorporado en nuestro desarrollo las líneas de código que obtienen un número aleatorio basado en el algoritmo SHA1PRNG, bastará con sustituir las líneas:
Long numeroAleatorio = SecureRandom.getInstance(“SHA1PRNG”).nextLong());
por las siguientes:
Long numeroAleatorio = new SecureRandom().nextLong();
Sin embargo, si no tenemos acceso a los fuentes del código para cambiarlo, tal y como ocurre en el arranque del subsistema security en Weblogic; la solución es modificar el valor de la opción securerandom.source en el fichero java.securityde la instalación de la JRE con el siguiente valor:
securerandom.source=file:/dev/./urandom
De esta forma, la JRE no detectará la cadena /dev/urandom y por tanto no procederá a sustituirla por /dev/random. Sin embargo, en unix, la ruta /dev/./urandom hace referencia al mismo fichero que la ruta /dev/urandom, por lo que el funcionamiento será el mismo y el rendimiento será el óptimo.
Y así, es como finaliza esta investigación que viene a demostrar las infinitas complejidades que surgen en los desarrollos basados en Java.
Espero que os haya sido útil e interesante.