Sesiones pegajosas se refiere a la necesidad de redirigir las peticiones de un determinado usuario al mismo servidor en el que se encuentra su sesión. Se considera un antipatrón, ya que en caso de fallo de un servidor, todos los usuarios conectados a él perderán sus sesiones.
La asociación entre el usuario y el servidor suele configurarse a través del equilibrador de carga, y estrategias sencillas de equilibrio de carga como Redondo-robin a menudo no son suficientes para asegurar una distribución uniforme de las peticiones, ya que los usuarios pesados podrían acabar todos en el mismo nodo. Hay muchas maneras de evitar las sticky sessions, pero si tu aplicación almacena los datos del usuario en la HTTPSession, las opciones sin requerir una refactorización sustancial son un poco limitadas.
Una solución rápida a este problema es almacenar la sesión en la base de datos en lugar de utilizar la memoria del servidor. En este escenario, no importa qué nodo reciba la petición, cargará la sesión del usuario directamente desde el almacén de datos. Este enfoque es más simple que las soluciones específicas de contenedor, y también le permiten consultar las sesiones como cualquier otra cosa en su base de datos.
Couchbase encaja particularmente bien en este escenario: utiliza el motor interno de clave-valor y también aprovecha la capa de caché interna para mantener en memoria las sesiones utilizadas recientemente. En la práctica, esto significa que se trata de una solución que funcionará bien incluso a escala. Por eso añadimos el soporte de la comunidad Sesión de primavera:
Sesión Couchbase Spring hace que sea trivial soportar sesiones agrupadas almacenándolas en la base de datos y desde el punto de vista del desarrollador es totalmente transparente. Todo lo que tienes que hacer es añadir la siguiente dependencia:
|
1 2 3 4 5 |
<dependency> <groupId>io.github.couchbaselabs</groupId> <artifactId>spring-session-data-couchbase</artifactId> <version>1.1</version> </dependency> |
y luego, en su clase principal, añada la clase @EnableCouchbaseHttpSession anotación:
|
1 2 3 4 5 6 7 8 9 |
@SpringBootApplication @EnableCouchbaseHttpSession public class SessionStoreApplication { public static void main(String[] args) { SpringApplication.run(SessionStoreApplication.class, args); } } |
y ya está!. Spring guardará automáticamente la HTTPSession en el Couchbase a partir de ahora:
|
1 2 3 4 5 6 7 8 |
@GetMapping("/newSession") public String newSession(HttpServletRequest request, Model model) throws Exception { request.getSession().invalidate(); HttpSession newSession = request.getSession(); newSession.setAttribute("foo", new Foo("key", "value")); return defaultPage(model, newSession); } |
Por defecto, la sesión se almacenará en la base de datos en un documento con un tipo igual a "sesiones“:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
//key : 5b357ade-6059-4d16-aea3-6f784765e7b5 { "_principal": null, "_interval": 1800, "_expireAt": 1554743279889, "_created": 1554741479889, "_accessed": 1554741479889, "_type": "sessions", "_attr": "\"rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABdAADZm9vc3IAHWNvbS5jYi5zZXNzaW9uc3RvcmUubW9kZWwuRm9vO5F+XaK9pV0CAAJMAAphdHRyaWJ1dGUxdAASTGphdmEvbGFuZy9TdHJpbmc7TAAKYXR0cmlidXRlMnEAfgAEeHB0AAZ2YWx1ZTF0AAZ2YWx1ZTJ4\"" } |
Pero puede cambiar el nombre del atributo de tipo, el valor de tipo y el tiempo que debe durar la sesión:
|
1 2 3 4 5 6 7 8 9 |
@SpringBootApplication @EnableCouchbaseHttpSession(typeName = "myType", typeValue = "myValueType", maxInactiveIntervalInSeconds = 1800) public class SessionStoreApplication { public static void main(String[] args) { SpringApplication.run(SessionStoreApplication.class, args); } } |
Consulta de la sesión del usuario
Tenga en cuenta que todos los datos de la sesión se almacenan de forma binaria en un atributo llamado _attr:
|
1 2 3 4 |
{ ... "_attr": "\"rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABdAADZm9vc3IAHWNvbS5jYi5zZXNzaW9uc3RvcmUubW9kZWwuRm9vO5F+XaK9pV0CAAJMAAphdHRyaWJ1dGUxdAASTGphdmEvbGFuZy9TdHJpbmc7TAAKYXR0cmlidXRlMnEAfgAEeHB0AAZ2YWx1ZTF0AAZ2YWx1ZTJ4\"" } |
Spring no sabe qué tipos de objetos hay en la sesión, por lo que no hay una forma sencilla de convertirla a un formato legible por humanos. Puede superar esta limitación estableciendo el atributo keepStringAsLiteral como verdadero en el EnableCouchbaseHttpSession anotación:
|
1 2 3 4 5 6 7 8 9 |
@SpringBootApplication @EnableCouchbaseHttpSession(keepStringAsLiteral = true) public class SessionStoreApplication { public static void main(String[] args) { SpringApplication.run(SessionStoreApplication.class, args); } } |
keepStringAsLiteral dirá Sesión Couchbase Spring para almacenar todos los atributos String de la sesión como propiedades de nivel superior dentro del documento. Por ejemplo, en lugar de añadir una instancia directamente a la sesión, podríamos convertir el objeto a un formato de cadena codificado en JSON utilizando la función de Jackson ObjectMapper:
|
1 2 |
ObjectMapper mapper = new ObjectMapper(); session.setAttribute("key", mapper.writeValueAsString(myClassInstance)) |
Y luego, cuando necesites leer el carro de sesión, conviértelo de nuevo en un objeto:
|
1 2 |
ObjectMapper mapper = new ObjectMapper(); mapper.readValue( session.getAttribute("key").toString(), MyClass.class); |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
//key : 5b2a2487-4825-43de-b089-1b61703556b2 { "_principal": null, "_interval": 1800, "_expireAt": 1554746972015, "_created": 1554745163803, "_accessed": 1554745172015, "key": "{\"shoppingCart\":{\"created\":1554745170784,\"items\":[{\"itemName\":\"Tennis Shoes\",\"price\":38.25186017511709,\"quantity\":3}]},\"user\":{\"username\":\"robertst\",\"phoneNumber\":\"(500)-383-1668\"},\"location\":{\"address\":\"90 Arrowhead Avenue Jonesboro, GA 30236\",\"country\":\"USA\",\"coordinates\":{\"lat\":10,\"lon\":37}}}", "_type": "sessions", "_attr": "\"rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeA==\"" } |
Tenga en cuenta que todavía tenemos el _att para los objetos que no son cadenas. Sin embargo, ahora también tenemos un atributo llamado llave, que es exactamente el objeto que añadimos a la sesión en el ejemplo anterior.
Ahora, si quieres consultar tu sesión, N1QL tiene una función llamada DECODE_JSONque puede convertir una cadena codificada en JSON en un objeto:
|
1 2 3 4 5 |
SELECT meta().id as id, _created, ARRAY_COUNT(DECODE_JSON(sessionCart).shoppingCart.items) FROM sessionstore ORDER BY _created DESC LIMIT 10 |
Nota: En un entorno de producción, se recomienda crear un índice con el objeto descodificado en lugar de descodificarlo en el momento de la consulta.
Si desea obtener más información sobre Couchbase Spring Session, consulte lo siguiente tutorial
Si tiene alguna pregunta, no dude en ponerse en contacto conmigo en @deniswrosa