Sessões fixas refere-se à necessidade de redirecionar as solicitações de um determinado usuário para o mesmo servidor em que sua sessão reside. Isso é considerado um antipadrão, pois, em caso de falha do servidor, todos os usuários conectados a ele perderão suas sessões.
A associação entre o usuário e o servidor geralmente é configurada por meio do balanceador de carga, e estratégias simples de balanceamento de carga, como Redondo-robin muitas vezes não são suficientes para garantir uma distribuição uniforme das solicitações, pois os usuários pesados podem acabar todos no mesmo nó. Há muitas maneiras de evitar as sessões fixas, mas se o seu aplicativo armazena os dados do usuário na sessão HTTPS, as opções sem a necessidade de uma refatoração substancial são um pouco limitadas.
Uma solução rápida para esse problema é armazenar a sessão no banco de dados em vez de usar a memória do servidor. Nesse cenário, não importa qual nó receba a solicitação, ele carregará a sessão do usuário diretamente do armazenamento de dados. Essa abordagem é mais simples do que as soluções específicas para contêineres e também permite que você consulte as sessões como qualquer outra coisa no seu banco de dados.
O Couchbase se encaixa particularmente bem nesse cenário: ele usa o mecanismo interno de valor-chave e também aproveita a camada de cache interna para manter na memória as sessões usadas recentemente. Na prática, isso significa que essa é uma solução que terá um bom desempenho mesmo em escala. É por isso que estamos adicionando o suporte da comunidade Sessão de primavera:
Sessão do Couchbase Spring torna trivial o suporte a sessões em cluster, armazenando-o no banco de dados e, do ponto de vista do desenvolvedor, é totalmente transparente. Tudo o que você precisa fazer é adicionar a seguinte dependência:
|
1 2 3 4 5 |
<dependency> <groupId>io.github.couchbaselabs</groupId> <artifactId>spring-session-data-couchbase</artifactId> <version>1.1</version> </dependency> |
e, em seguida, em sua classe principal, adicione a tag @EnableCouchbaseHttpSession anotação:
|
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); } } |
e pronto! De agora em diante, o Spring salvará automaticamente a HTTPSession no Couchbase:
|
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 padrão, a sessão será armazenada no banco de dados em um documento com um tipo igual a "sessões“:
|
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\"" } |
Mas você pode alterar o nome do atributo de tipo, o valor do tipo e a duração da sessão:
|
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); } } |
Consultar a sessão do usuário
Observe que todos os dados da sessão são armazenados de forma binária em um atributo chamado _attr:
|
1 2 3 4 |
{ ... "_attr": "\"rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABdAADZm9vc3IAHWNvbS5jYi5zZXNzaW9uc3RvcmUubW9kZWwuRm9vO5F+XaK9pV0CAAJMAAphdHRyaWJ1dGUxdAASTGphdmEvbGFuZy9TdHJpbmc7TAAKYXR0cmlidXRlMnEAfgAEeHB0AAZ2YWx1ZTF0AAZ2YWx1ZTJ4\"" } |
O Spring não sabe quais tipos de objetos estão na sessão, portanto, não há uma maneira fácil de convertê-lo em um formato legível por humanos. Você pode superar essa limitação definindo o atributo manterStringAsLiteral como verdadeiro no EnableCouchbaseHttpSession anotação:
|
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); } } |
manterStringAsLiteral dirá Sessão do Couchbase Spring para armazenar todos os atributos String da sessão como propriedades de nível superior no documento. Por exemplo, em vez de adicionar uma instância diretamente à sessão, poderíamos converter o objeto em um formato String codificado em JSON usando a função Mapeador de objetos:
|
1 2 |
ObjectMapper mapper = new ObjectMapper(); session.setAttribute("key", mapper.writeValueAsString(myClassInstance)) |
E então, quando você precisar ler o carrinho de sessão, converta-o novamente em um 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==\"" } |
Observe que ainda temos o _att para objetos que não sejam uma String. No entanto, agora também temos um atributo chamado chave, que é exatamente o objeto que adicionamos à sessão no exemplo anterior.
Agora, se você quiser consultar sua sessão, N1QL tem uma função chamada DECODE_JSONque pode transformar uma string codificada em JSON em um 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 |
Observação: Em um ambiente de produção, recomendamos que você crie um índice com o objeto decodificado em vez de decodificá-lo no momento da consulta.
Se você quiser saber mais sobre o Couchbase Spring Session, confira este tutorial
Se tiver alguma dúvida, sinta-se à vontade para entrar em contato comigo pelo e-mail @deniswrosa