Prefacio
Solo para los desarrolladores de Java, debemos estar familiarizados con la sesión en la especificación J2EE. Para la mayoría de los desarrolladores que usan TomCat como contenedor web, ¿cómo implementa Tomcat Session para marcar a los usuarios y administrar la información de la sesión?
resumen
SESIÓN
Tomcat define internamente las interfaces relacionadas con la sesión y la httpsession, el sistema de herencia de clase se muestra en la Figura 1.
Figura 1 Sistema de herencia de clase de sesión
La Figura 1 enumera además el sistema de herencia de clase de sesión, y se introducen uno por uno aquí.
Sesión: la especificación de interfaz básica para las sesiones en Tomcat.
Tabla 1 Descripción de la interfaz de sesión
método | describir |
getCreationTime ()/setCreationTime (tiempo: largo) | Obtenga y establezca la hora de creación de la sesión |
getId ()/setid (id: cadena) | Obtenga y establezca la identificación de la sesión |
getThisAsCcessTime () | Obtenga la hora de inicio de la última solicitud |
getLastAccessTime () | Obtenga el tiempo de finalización de la última solicitud |
getManager ()/setManager (gerente: gerente) | Obtener y configurar el gerente de sesión |
getMaxInactiveInterval ()/setMaxInactiveInterval (Intervalo: int) | Obtenga el intervalo de acceso máximo entre configurar la sesión |
getSession () | Obtener httpsession |
isValid ()/setValid (isValid: boolean) | Obtenga y establezca el estado válido de la sesión |
access ()/endAccess () | Iniciar y finalizar el acceso a la sesión |
expirar() | Establezca el vencimiento de la sesión |
Httpsession: una especificación de interfaz para una sesión proporcionada por un cliente HTTP y un servidor HTTP.
Tabla 2 Descripción de la interfaz httpsession
método | describir |
GetCreationTime () | Obtenga el tiempo de creación de la sesión |
getID () | Obtenga la identificación de la sesión |
getLastAccessTime () | Obtenga el tiempo de finalización de la última solicitud |
getServletContext () | Obtenga el servletContext a la que pertenece la sesión actual |
getMaxInactiveInterval ()/setMaxInactiveInterval (Intervalo: int) | Obtenga el intervalo de acceso máximo entre configurar la sesión |
getAttribute (nombre: string) /setattribute (nombre: string, valor: objeto) | Obtenga y establezca las propiedades del alcance de la sesión |
RemoveAttribute (nombre: cadena) | Borrar propiedades del alcance de la sesión |
invalidar() | Invalidar la sesión y deshacer cualquier objeto vinculado a esta sesión |
Clustersession: la especificación de la interfaz de sesión en la implementación de clúster.
Tabla 3 Descripción de la interfaz de clustersession
método | describir |
isPrimarySession () | ¿Es la sesión principal del clúster? |
setPrimarySession (primaria booleana) | Configurar la sesión maestra de clúster |
Estándares: una implementación estándar de la sesión HTTP, este artículo utilizará esta implementación como ejemplo.
Al implementar un clúster Tomcat, el estado de la sesión de cada nodo en el clúster debe mantenerse sincronizado actualmente.
Asesión replicada: cada vez, todo el objeto de sesión se sincroniza con otros nodos en el clúster, y los otros nodos actualizan todo el objeto de sesión. Esta implementación es relativamente simple y conveniente, pero causará la transmisión de una gran cantidad de información no válida.
Deltassion: sincroniza las propiedades modificadas incrementalmente en la sesión. Dado que este método es incremental, reducirá en gran medida la sobrecarga de la E/S de la red, pero la implementación será más complicada porque implica la gestión de los procesos de operación de atributos de sesión.
Gerente de sesión
Tomcat define internamente la interfaz del administrador para formular las especificaciones de la interfaz del administrador de la sesión.
Figura 2 Sistema de herencia de clase del administrador de sesiones
Describiremos el contenido correspondiente en la Figura 2 uno por uno a continuación:
Gerente: TomCat Para la especificación de la interfaz definida por el administrador de la sesión, la Figura 2 ha enumerado los métodos principales definidos en la interfaz del administrador, y la Tabla 4 describe el papel de estos métodos en detalle.
Tabla 4 Descripción de la interfaz del administrador
método | describir |
getContainer ()/setContainer (contenedor: contenedor) | Obtenga o establezca el contenedor asociado con el administrador de la sesión, generalmente un contenedor de contexto |
getDistributable ()/setDistributable (distribuible: boolean) | Obtenga o establezca si el administrador de sesión admite distribuido |
getMaxInactiveInterval ()/setMaxInactiveInterval (Intervalo: int) | Obtenga o establezca el intervalo inactivo máximo para la sesión creada por el gerente de la sesión |
getSessionIdLength ()/setSessionIdLength (idlength: int) | Obtenga o establezca la duración de la ID de sesión creada por el administrador de la sesión |
getSessionCounter ()/setSessionCounter (Sessioncounter: Long) | Obtener o establecer el número total de sesiones creadas por el gerente de sesión |
getMaxactive ()/setMaxactive (Maxactive: int) | Obtener o establecer el número máximo de sesiones actualmente activadas |
getActiveSesions () | Obtenga todas las sesiones actualmente activadas |
GetEppiredSessions ()/SetExperedSessions (expiredSessions: Long) | Obtener o establecer el número de sesiones caducadas actualmente |
getRegectedSessions ()/setRectedSessions (rechazado | Obtenga o establezca el número de sesiones que se han negado para ser creadas |
getSessionMaxaliveTime ()/setsessionMaxaliveTime (sessionMaxaliveTime: int) | Obtiene o establece la duración máxima de la actividad en una sesión vencida |
getSessionVerageAliveTime ()/setSessionVerageAliveTime (SessionAvergeAtealiveTime: int) | Obtenga o establezca la duración promedio de la actividad para una sesión vencida |
Agregar (sesión: sesión)/eliminar (sesión: sesión) | Agregar o eliminar sesiones activas al administrador de la sesión |
ChangesessessId (Sesión: Sesión) | Establezca la ID de sesión aleatoria recién generada para la sesión |
CreateSession (SessionID: String) | Cree una nueva sesión basada en la configuración de atributo predeterminada del administrador de sesiones |
findsession (id: cadena) | Devuelve la sesión con la marca única del parámetro SessionID |
FindSessions () | Devuelve todas las actividades administradas por el gerente de sesión |
Load ()/Descargue () | Sesión de carga desde el mecanismo de persistencia o la sesión de escritura al mecanismo de persistencia |
backgroundprocess () | La interfaz del contenedor se define como la implementación de un trabajo relacionado con el procesamiento de contenedores específico en segundo plano. |
ManagerBase : encapsula una clase abstracta que es comúnmente implementada por la interfaz del administrador. Todos los gerentes de sesión se heredan de ManagerBase.
ClusterManager : Se agregó algunas interfaces bajo la implementación de clúster basada en la interfaz del administrador.
PersistentManagerBase: proporciona una implementación básica de la persistencia de la sesión.
PersistentManager: Heredado de PersistentManagerBase, se puede utilizar configurando el elemento <Store> en server.xml. PersistentManager puede hacer una copia de seguridad de la información de la sesión en la memoria a un archivo o base de datos. Cuando un objeto de sesión está respaldado, el objeto de sesión se copia en la memoria (archivo o base de datos), mientras que el objeto original permanece en la memoria. Por lo tanto, incluso si el servidor cae, el objeto de sesión activo aún se puede recuperar de la memoria. Si el objeto de sesión activo excede el límite superior o el objeto de sesión está inactivo durante demasiado tiempo, la sesión se cambiará a la memoria para guardar espacio de memoria.
StandardManager: no es necesario configurar el elemento <Store>. . Cuando Tomcat se reinicia o se carga la aplicación, Tomcat restaurará la sesión en el archivo a la memoria. Si el servidor termina repentinamente, todas las sesiones se perderán porque StandardManager no tiene la oportunidad de implementar el procesamiento de guardado.
ClusterManagerBase: proporciona implementación de gestión de clúster para la sesión.
Deltamanager: heredado de ClusterManagerBase. Este administrador de sesión es el administrador predeterminado de Tomcat en la implementación del clúster.
BackupManager: no herede ClusterManagerBase, pero implementa directamente la interfaz ClusterManager. Es un administrador de sesión opcional para TomCat en la implementación del clúster. Todos los nodos en el clúster pueden acceder a este nodo de respaldo para lograr el efecto de copia de seguridad de la sesión en el clúster.
Para simplificar, este artículo utiliza StandardManager como ejemplo para explicar la gestión de la sesión. StandardManager es un componente infantil de StandardContext, utilizado para administrar la creación y mantenimiento de todas las sesiones del contexto actual. Si debe leer o estar familiarizado con el contenido del artículo "Análisis del código fuente de Tomcat - Gestión del ciclo de vida", entonces sabrá que cuando StandardContext se lance oficialmente, es decir, el método Startinternal de inicio de StandardContext (ver Listado 1) se llama StandardContext, todavía comenzará StandardManager.
Listado de código 1
@Override sincronizado sincronizado startInternal () lanza lifecycleException {// omitir código que no está relacionado con la gestión de la sesión // adquirir gerente de clúster contextmanager = null; && Distributable) {try {contextManager = getCluster (). createmanager (getName ()); StandardManager (); Déjate saber al clúster que hay un contexto que se distribuye // y que tiene su propio gerente getCluster (). RegisterManager (Manager); if ((gerente! = null) && (instancia de gerente de lifecycle)) {((lifecycle) getManager ()). inicio (); .Error ("Manager de error. Start ()", e);
Desde el Listado 1, puede ver que los pasos de ejecución involucrados en la gestión de la sesión en el método StartInternal de StandardContext son los siguientes:
Crear StandardManager;
Si TomCat combina Apache para la implementación distribuida, el StandardManager actual se registrará en el clúster;
Iniciar StandardManager;
El método de inicio de StandardManager se utiliza para iniciar StandardManager, y la implementación se muestra en el Listado 2 del código.
Listado de código 2
@Override public sincronizado Final Void Start () lanza LifeCycleException {// omitir el código para la verificación de estado if (state.equals (lifecyclestate.new)) {init (); !state.equals(LifecycleState.STOPPED)) { invalidTransition(Lifecycle.BEFORE_START_EVENT); } setState(LifecycleState.STARTING_PREP); try { startInterna l(); } catch (LifecycleException e) { setState(LifecycleState.FAILED); throw e; } if (state.equals (lifecyClestate.failed) || state.equals (lifecyClestate.must_stop)) {stop (); Haciendo lo que se supone que deben.
Desde el Listado 2, podemos ver que los pasos para iniciar StandardManager son los siguientes:
Llame al método init para inicializar el StandardManager;
Llame al método Startinternal para iniciar StandardManager;
Inicialización de StandardManager
Después del análisis anterior, sabemos que el primer paso para iniciar StandardManager es llamar al método Init de la base de vida de la clase principal. preocuparse por la iniciativa de StandardManager. StandardManager en sí no implementa el método Initinternal, pero la base de la clase matriz de StandardManager implementa este método, consulte el Listado 3 para su implementación.
Listado de código 3
@Override void initinternal () lanza lifecycleException {super.initinternal ();
Lista de lectura del Código 3, resumimos los pasos de ejecución del método Initinternal de ManagerBase:
Registre el contenedor en sí, el StandardManager, a JMX (consulte el artículo "Análisis del código fuente de Tomcat - Gestión del ciclo de vida" para la implementación del método inicial de LifecyclembeanBase);
Obtenga el TomCat actual del contenedor principal StandardContext y configúrelo en la propiedad booleana de ManagerBase Distributable;
Llame al método getRandombytes para obtener una matriz de bytes aleatoria del archivo de número aleatorio /dev /urandom.
Nota: La matriz de bytes aleatoria generada al llamar al método GetRandombytes aquí no se utilizará.
Leemos la implementación del código del método GetRandombytes en detalle, consulte el Listado 4 del código.
Listado de código 4
GetRandombytes vacío protegido (byte bytes []) {// Generar una matriz de bytes que contenga un identificador de sesión if (devRandomSource! = null && randomis == null) {set randomFile (devRandomSource); int len = Randomis.read (bytes); Catch (excepción ex) {// ignorar} DevRandomSource = NULL; ) .nextbytes (bytes);
setRandomFile en el listado 4
Los métodos (ver Listado 5 para el código) se utilizan para obtener una matriz aleatoria de bytes del archivo de número aleatorio /dev /urandom.
Lista del código 5
public void setRandomFile (String s) {// Como hack, puede usar un archivo estático, y generar las mismas // ID de sesión (buenas para la depuración extraña) if (globals.is_se curity_enabled) {randomis = accessController.doPrivileged (nuevo PrivilegedSetRandomFile (S)); readLong (); ) {try {RandomIs.Close ();
El método SetRandomFile en el Listado 4 (ver Listado 6) genera una instancia de java.security.secureerandom a través de la reflexión, y utiliza esta instancia para generar una serie de bytes aleatorios.
Listado de código 6
public Random getRandom () {if (this.random == null) {// Calcula el nuevo número de número de número ); // construye y sembra una nueva clase de generador de números aleatorios <?> Clazz = class.forname (RandomClass); e) {// Vuelve al caso simple log.error (sm.getString ("gereMBase.random", RandomClass), e); (semilla)); ) + "" + (t2-t1));
Según el análisis anterior, la inicialización de StandardManager ejecuta principalmente el método inicial de ManagerBase.
Inicio de StandardManager
Llamar al método de inicio de StandardManager se utiliza para iniciar StandardManager, consulte el Listado 7.
Listado de código 7
@Override sincronizado void startInternal () lanza LifeCycleException {// Force Inicialización del generador de números aleatorios if (log.IsdeBugenable D ()) log.debug ("Inicialización de número aleatorio de fuerza que comienza"); ISDEBUGENABLED ()) LOG.DEBUG ("Force el número aleatorio de inicialización completada"); Managerload "), t);} setState (lifecyClestate.starting);}
Desde el Listado 7, podemos ver que los pasos para iniciar StandardManager son los siguientes:
Paso 1: Llame al método GeneratesIsSion (consulte el Listado 8) para generar una nueva ID de sesión;
Listado de código 8
Cadena sincronizada GeneratedSessionId () {Byte Random [] = New Byte [16]; do {int resultlenBytes = 0; ; aleatorio [j] & 0x0f); ; si (b2 <10) buffer.append ((char) ('0' + b2)); JVMROUTE! :::::::::::::::::: para::: ::::::::::::::::::::::::::::::: :::::::::::::::::: para::: ::::::::::::::::::::::::::::::: :::::::::::::::::: para ::::::
El paso 2 carga la información persistente de la sesión. ¿Por qué debe persistir la sesión? Dado que todas las sesiones se mantienen en un ConcurrentHashmap en StandardManager, el reinicio del servidor o el tiempo de inactividad hace que esta información de la sesión se pierda o sea inválida. Echemos un vistazo a la implementación del método de carga de StandardManager, consulte el Listado 9 del código.
Listado de código 9
public void load () lanza ClassNotFoundException, IOException {if (SecurityUtIl.IspAckageProtectionEnabled ()) {try {AccessController.DopR. Excepción InstanceOf ClassNotFoundException) {Throw (ClassNotFoundException) Excepción; ;
Si se requiere que el mecanismo de seguridad se encienda y se active el modo de protección del paquete, la sesión persistida se cargará creando un Doload privilegiado, que se implementa como se muestra en el Listado 10 del código.
Listado 10
La clase privada privilegeddoload implementa privilegedExceptionAction <Void> {privilegedDoload () {// noop} public void run () lanza la excepción {do load ();
Desde el Listado 10, podemos ver que el método que es realmente responsable de la carga es DOLOAD. Por lo tanto, solo necesitamos observar la implementación de Doload, consulte el Listado 11 del código.
Listado 11
protegido doload () lanza ClassNotFoundException, IOException {if (log.isDebugeNabled ()) log.debug ("Inicio: Carga de sesiones persistidas"); a la ruta especificada, si cualquier archivo de archivo = archivo (); FileInputStream Fis = NULL; Container! Creación de transmisión de entrada de objeto personalizado para el cargador de clases "); ois = new CustomObjectInputStream (bis, classloader);} else {if (log.isdebugeNabled ()) log.debug (" Creación de flujo de entrada de ob ject estándar "); ois = nuevo ObjectInputStream (BIS); .getString ("StandardManager.Loading .ioe", e), e); nulo) {try {bis. readObject (); {Sessionsion Session = GetNeWSession (); ) {// Si la sesión ya es inválida, // Session para evitar la fuga de memoria. getString ("StandardManager.Loading.cnfe", e), e); .getString ("StandardManager.Loading. IOE", E), E); OIS.CLOSE (); Debug ("Finalización: Loa Ding persistió sesiones");
Desde el Listado 11, consulte los pasos de ejecución del método StandardManager DOLOAD son los siguientes:
Borre la información de la sesión mantenida por Sessions Cache;
Llame al método de archivo para devolver el archivo persistente de la sesión en el contexto actual, como: d: /workspace/tomcat7.0/work/catalina/localhost/host-manager/sessions.ser;
Abra el flujo de entrada del archivo persistente de la sesión y lo encapsule como customObjectInputStream;
Lea el número de sesiones persistidas del archivo persistente de la sesión, y luego lea la información de la sesión una por una y póngala en el caché de las sesiones.
En este punto, la introducción al lanzamiento de StandardManager está aquí.